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:
unsigned arr[10];
std::mt19937 myEngine;
std::generate_n( arr, 10, std::ref(myEngine) ); // Modifies myEngine's state
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:
std::ref(myEngine)() // Valid expression, modifies myEngines state
… (Des) como referências comuns, copiar (e atribuir) reference_wrappers
apenas atribui a ponta.
int i, j;
auto r = std::ref(i); // r refers to i
r = std::ref(j); // Okay; r refers to j
r = std::cref(j); // Error: Cannot bind reference_wrapper<int> to <const int>
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 paraoperator()
) devem ser apenas embutidas, pois são de uma linha.
reference_wrapper
s são criados via std::ref
estd::cref
:
int i;
auto r = std::ref(i); // r is of type std::reference_wrapper<int>
auto r2 = std::cref(i); // r is of type std::reference_wrapper<const int>
O argumento template especifica o tipo e qualificação cv do objeto referido; r2
refere-se a a const int
e somente produzirá uma referência a const int
. Chamadas para referenciar wrappers com const
functores neles só chamarão a const
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 resultado tuple
passando o argumento correspondente por meio de reference_wrapper
:
int i;
auto t1 = std::make_tuple(i); // Copies i. Type of t1 is tuple<int>
auto t2 = std::make_tuple(std::ref(i)); // Saves a reference to i.
// Type of t2 is tuple<int&>
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 um reference_wrapper
. Útil se aquele argumento (ou o functor!) Não precisar ser copiado, mas permanecer no escopo enquanto o bind
-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.
int i;
int& ref = std::ref(i); // Okay
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
.
std::reference_wrapper<int> r; // Invalid
Uma semelhança é a semântica de cópia superficial: Ponteiros e reference_wrapper
s podem ser reatribuídos.
.
com em vez de->