Tenho algumas perguntas sobre este programa:
#include <iostream>
#include <type_traits>
#include <functional>
using namespace std;
template <typename T> void foo ( T x )
{
auto r=ref(x);
cout<<boolalpha;
cout<<is_same<T&,decltype(r)>::value;
}
int main()
{
int x=5;
foo (x);
return 0;
}
O resultado é:
false
Eu quero saber, se std::ref
não retorna a referência de um objeto, então o que ele faz? Basicamente, qual é a diferença entre:
T x;
auto r = ref(x);
e
T x;
T &y = x;
Além disso, quero saber por que existe essa diferença? Por que precisamos std::ref
ou std::reference_wrapper
quando temos referências (ou seja T&
)?
x = y;
em ambos os casos?Respostas:
O Well
ref
constrói um objeto doreference_wrapper
tipo apropriado para manter uma referência a um objeto. O que significa quando você se inscreve:auto r = ref(x);
Isso retorna a
reference_wrapper
e não uma referência direta ax
(ou sejaT&
). Estereference_wrapper
(isto ér
), em vez disso, é válidoT&
.A
reference_wrapper
é muito útil quando você deseja emular areference
de um objeto que pode ser copiado (é tanto construtível por cópia quanto atribuível por cópia ).Em C ++, uma vez que você criar uma referência (digamos
y
) a um objeto (digamosx
), em seguida,y
ex
compartilham o mesmo endereço de base . Além disso,y
não pode se referir a nenhum outro objeto. Além disso, você não pode criar uma matriz de referências, ou seja, um código como este gerará um erro:#include <iostream> using namespace std; int main() { int x=5, y=7, z=8; int& arr[] {x,y,z}; // error: declaration of 'arr' as array of references return 0; }
No entanto, isso é legal:
#include <iostream> #include <functional> // for reference_wrapper using namespace std; int main() { int x=5, y=7, z=8; reference_wrapper<int> arr[] {x,y,z}; for (auto a: arr) cout << a << " "; return 0; } /* OUTPUT: 5 7 8 */
Falando sobre seu problema com
cout << is_same<T&,decltype(r)>::value;
, a solução é:cout << is_same<T&,decltype(r.get())>::value; // will yield true
Deixe-me mostrar um programa:
#include <iostream> #include <type_traits> #include <functional> using namespace std; int main() { cout << boolalpha; int x=5, y=7; reference_wrapper<int> r=x; // or auto r = ref(x); cout << is_same<int&, decltype(r.get())>::value << "\n"; cout << (&x==&r.get()) << "\n"; r=y; cout << (&y==&r.get()) << "\n"; r.get()=70; cout << y; return 0; } /* Ouput: true true true 70 */
Veja aqui, nós conhecemos três coisas:
Um
reference_wrapper
objeto (aquir
) pode ser usado para criar uma matriz de referências que não foi possível comT&
.r
realmente age como uma referência real (veja comor.get()=70
mudou o valor dey
).r
não é o mesmo,T&
masr.get()
é. Isso significa quer
mantém,T&
ou seja, como o nome sugere, é um invólucro em torno de uma referênciaT&
.Espero que esta resposta seja mais do que suficiente para esclarecer suas dúvidas.
fonte
reference_wrapper
pode ser reatribuído , mas não pode "conter referência a mais de um objeto". 2/3: Ponto justo sobre onde.get()
é apropriado - mas sem sufixor
pode ser usado da mesma forma queT&
nos casos em quer
a conversão deoperator
pode ser invocada sem ambigüidade - portanto, não há necessidade de chamar.get()
em muitos casos, incluindo vários em seu código (que é difícil de ler devido à falta de espaços).reference_wrapper
pode conter um array de referências se você não tiver certeza, então você mesmo pode tentar. Plus.get()
é usado quando você deseja alterar o valor do objeto quereference_wrapper
está segurando, ou seja,r=70
é ilegal, então você deve usarr.get()=70
. Experimente !!!!!!int a[4]{1, 2, 3, 4}; int (&b)[4] = a;
? reference_wrapper não é especial aqui desde nativaT&
faz trabalho.wrapper
pode ir em um contêiner. Isso é útil, mas acho que as pessoas interpretam mal como algo mais avançado do que realmente é. Se eu quiser um array de 'refs', geralmente pulo o intermediáriovector<Item *>
, que é o que sewrapper
resume a ... e espero que os puristas anti-ponteiro não me encontrem. Os casos de uso convincentes são diferentes e mais complexos.std::reference_wrapper
é reconhecido pelas instalações padrão por ser capaz de passar objetos por referência em contextos de passagem por valor.Por exemplo,
std::bind
pode receber ostd::ref()
para algo, transmiti-lo por valor e descompactá-lo em uma referência posteriormente.void print(int i) { std::cout << i << '\n'; } int main() { int i = 10; auto f1 = std::bind(print, i); auto f2 = std::bind(print, std::ref(i)); i = 20; f1(); f2(); }
Este snippet produz:
10 20
O valor de
i
foi armazenado (tomado por valor)f1
no ponto em que foi inicializado, masf2
manteve umstd::reference_wrapper
valor por e, portanto, se comporta como se tivesse recebido umint&
.fonte
std::ref(T)
retorna astd::reference_wrapper
. É um pouco mais do que um ponteiro enrolado, mas é reconhecido pela biblioteca como "ei, eu deveria ser uma referência! Por favor, me transforme novamente em um quando você terminar de me passar".Uma referência (
T&
ouT&&
) é um elemento especial na linguagem C ++. Permite manipular um objeto por referência e possui casos de uso especiais na linguagem. Por exemplo, você não pode criar um contêiner padrão para conter referências:vector<T&>
está malformado e gera um erro de compilação.Por
std::reference_wrapper
outro lado, A é um objeto C ++ capaz de conter uma referência. Como tal, você pode usá-lo em contêineres padrão.std::ref
é uma função padrão que retorna umstd::reference_wrapper
em seu argumento. Na mesma ideia,std::cref
retornastd::reference_wrapper
a uma referência const.Uma propriedade interessante de a
std::reference_wrapper
é que ele possui umoperator T& () const noexcept;
. Isso significa que mesmo se for um objeto verdadeiro , ele pode ser convertido automaticamente para a referência que está mantendo. Então:operator T& () const noexcept;
ele, pode ser usado em qualquer lugar onde você possa usar uma referência, pois será automaticamente convertido para ela.fonte
operator T& ()
quais outras 2 respostas não mencionaram.