Por que é necessário ter std::reference_wrapper
? Onde deve ser usado? Como é diferente de um simples ponteiro? Como seu desempenho se compara a um simples ponteiro?
99
Por que é necessário ter std::reference_wrapper
? Onde deve ser usado? Como é diferente de um simples ponteiro? Como seu desempenho se compara a um simples ponteiro?
.
com em vez de->
.
não funciona da maneira que você sugere (a menos que em algum ponto a proposta de ponto da operadora seja adotada e integrada :))get()
função-membro ou com sua conversão implícita de volta ao tipo subjacente.Respostas:
std::reference_wrapper
é útil em combinação com modelos. Ele envolve um objeto armazenando um ponteiro para ele, permitindo a reatribuição e a cópia, enquanto imita sua semântica usual. Também instrui determinados modelos de biblioteca a armazenar referências em vez de objetos.Considere os algoritmos no STL que copiam functores: Você pode evitar essa cópia simplesmente passando um wrapper de referência referindo-se ao functor em vez do próprio functor:
Isso funciona porque ...
...
reference_wrapper
são sobrecarregadosoperator()
para que possam ser chamados da mesma forma que os objetos de função aos quais se referem:… (Des) como referências comuns, copiar (e atribuir)
reference_wrappers
apenas atribui a ponta.Copiar um invólucro de referência é praticamente equivalente a copiar um ponteiro, que é o mais barato possível. Todas as chamadas de função inerentes ao seu uso (por exemplo, aquelas para
operator()
) devem ser apenas embutidas, pois são de uma linha.reference_wrapper
s são criados viastd::ref
estd::cref
:O argumento template especifica o tipo e qualificação cv do objeto referido;
r2
refere-se a aconst int
e somente produzirá uma referência aconst int
. Chamadas para referenciar wrappers comconst
functores neles só chamarão aconst
função de membrooperator()
.Os inicializadores Rvalue não são permitidos, pois permiti-los faria mais mal do que bem. Uma vez que rvalues seriam movidos de qualquer maneira (e com eliminação de cópia garantida mesmo que seja evitada parcialmente), não melhoramos a semântica; podemos introduzir ponteiros pendentes, pois um invólucro de referência não prolonga a vida útil do ponteiro.
Interação da biblioteca
Como mencionado antes, pode-se instruir
make_tuple
a armazenar uma referência no resultadotuple
passando o argumento correspondente por meio dereference_wrapper
:Observe que isso é um pouco diferente de
forward_as_tuple
: Aqui, rvalues como argumentos não são permitidos.std::bind
mostra o mesmo comportamento: não copia o argumento, mas armazena uma referência se for umreference_wrapper
. Útil se aquele argumento (ou o functor!) Não precisar ser copiado, mas permanecer no escopo enquanto obind
-functor é usado.Diferença de ponteiros comuns
Não há nível adicional de indireção sintática. Os ponteiros devem ser referenciados para obter um valor para o objeto ao qual se referem;
reference_wrapper
s têm um operador de conversão implícito e podem ser chamados como o objeto que envolvem.reference_wrapper
s, ao contrário dos ponteiros, não têm um estado nulo. Eles devem ser inicializados com uma referência ou outrareference_wrapper
.Uma semelhança é a semântica de cópia superficial: Ponteiros e
reference_wrapper
s podem ser reatribuídos.fonte
std::make_tuple(std::ref(i));
superior destd::make_tuple(&i);
alguma forma?i
, não uma referência a ele.Existem, pelo menos, dois objetivos motivadores de
std::reference_wrapper<T>
:É fornecer semântica de referência para objetos passados como parâmetro de valor para modelos de função. Por exemplo, você pode ter um grande objeto de função que deseja passar para o
std::for_each()
qual recebe seu objeto de função parâmetro por valor. Para evitar copiar o objeto, você pode usarPassar argumentos
std::reference_wrapper<T>
para umastd::bind()
expressão é bastante comum para vincular argumentos por referência em vez de por valor.Ao usar um
std::reference_wrapper<T>
comstd::make_tuple()
o elemento de tupla correspondente torna-se um emT&
vez de umT
:fonte
fun
é um objeto de função (ou seja, um objeto de uma classe com um operador de chamada de função) e não uma função: sefun
for uma função real,std::ref(fun)
não tem propósito e torna o código potencialmente mais lento.Outra diferença, em termos de código
reference_wrapper
autodocumentado , é que o uso de um essencialmente nega a propriedade do objeto. Em contraste, umunique_ptr
declara propriedade, enquanto um ponteiro simples pode ou não ser propriedade (não é possível saber sem olhar muitos códigos relacionados):fonte
reference_wrapper
é superior aos ponteiros brutos não apenas porque é claro que não é proprietário, mas também porque não pode sernullptr
(sem travessuras) e, portanto, os usuários sabem que não podem passarnullptr
(sem travessuras) e você sabe que não precisa verifique se há.Você pode pensar nisso como um invólucro conveniente em torno das referências para que possa usá-las em contêineres.
É basicamente uma
CopyAssignable
versão deT&
. Sempre que quiser uma referência, mas tem que ser atribuível, usestd::reference_wrapper<T>
ou sua função auxiliarstd::ref()
. Ou use um ponteiro.Outras peculiaridades
sizeof
::E comparação:
fonte
reference_wrapper
código trivial , tornando-o idêntico ao código que usa um ponteiro ou referência.std::reference_wrapper
tem a garantia de que o objeto nunca é nulo. Considere um alunostd::vector<T *>
. Você tem que examinar todo o código da classe para ver se esse objeto pode armazenar anullptr
no vetor, enquanto comstd::reference_wrapper<T>
, você tem a garantia de ter objetos válidos.