Ik heb enkele vragen over dit programma:
#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;
}
De uitvoer is:
false
Ik wil weten of std::ref
de referentie van een object niet teruggeeft, wat doet het dan? Wat is eigenlijk het verschil tussen:
T x;
auto r = ref(x);
en
T x;
T &y = x;
Ik wil ook weten waarom dit verschil bestaat? Waarom hebben we std::ref
of std::reference_wrapper
nodig als we referenties hebben (d.w.z. T&
)?
Antwoord 1, autoriteit 100%
Nou ref
construeert een object van het juiste type reference_wrapper
om een verwijzing naar een object te bevatten. Dat betekent wanneer je solliciteert:
auto r = ref(x);
Dit retourneert een reference_wrapper
en geen directe verwijzing naar x
(dwz T&
). Deze reference_wrapper
(dwz r
) bevat in plaats daarvan T&
.
Een reference_wrapper
is erg handig wanneer u een reference
van een object wilt emuleren dat kan worden gekopieerd (het is beide copy-constructibleen toewijsbaar).
In C++, als je eenmaal een verwijzing hebt gemaakt (zeg y
) naar een object (zeg x
), dan y
en x
hetzelfde basisadresdelen. Bovendien kan y
niet naar een ander object verwijzen. U kunt ook geen array van verwijzingenmaken, dus code als deze zal een foutmelding geven:
#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;
}
Dit is echter legaal:
#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
*/
Over je probleem met cout << is_same<T&,decltype(r)>::value;
, de oplossing is:
cout << is_same<T&,decltype(r.get())>::value; // will yield true
Laat me je een programma laten zien:
#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
*/
Kijk hier leren we drie dingen kennen:
-
Een
reference_wrapper
object (hierr
) kan worden gebruikt om een array van referentieste maken wat niet mogelijk was metT&
. -
r
gedraagt zich eigenlijk als een echte referentie (zie hoer.get()=70
de waarde vany
heeft gewijzigd) . -
r
is niet hetzelfde alsT&
maarr.get()
is dat wel. Dit betekent datr
T&
bevat, dwz zoals de naam al doet vermoeden is het een omhulsel rond een referentieT&
.
Ik hoop dat dit antwoord meer dan genoeg is om je twijfels uit te leggen.
Antwoord 2, autoriteit 54%
std::reference_wrapper
wordt herkend door standaardfaciliteiten om objecten door middel van referentie te kunnen doorgeven in pass-by-value-contexten.
std::bind
kan bijvoorbeeld de std::ref()
naar iets meenemen, het op waarde verzenden en het later weer uitpakken in een referentie aan.
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();
}
Dit fragment geeft het volgende weer:
10
20
De waarde van i
is opgeslagen (op waarde genomen) in f1
op het moment dat deze werd geïnitialiseerd, maar f2
heeft een std::reference_wrapper
op waarde, en gedraagt zich dus alsof het een int&
kostte.
Antwoord 3, autoriteit 40%
Een verwijzing (T&
of T&&
) is een speciaal element in de C++-taal. Het maakt het mogelijk om een object door verwijzingte manipuleren en heeft speciale gebruiksgevallen in de taal. U kunt bijvoorbeeld geen standaardcontainer maken voor verwijzingen: vector<T&>
is slecht gevormd en genereert een compilatiefout.
Een std::reference_wrapper
aan de andere kant is een C++-object dat een referentie kan bevatten. Als zodanig kun je het in standaard containers gebruiken.
std::ref
is een standaardfunctie die een std::reference_wrapper
retourneert op zijn argument. In hetzelfde idee retourneert std::cref
std::reference_wrapper
naar een const-referentie.
Een interessante eigenschap van een std::reference_wrapper
is dat het een operator T& () const noexcept;
. Dat betekent dat zelfs als het een echt object is, het automatisch kan worden geconverteerd naar de referentie die het bevat. Dus:
- omdat het een object is dat kan worden gekopieerd, kan het worden gebruikt in containers of in andere gevallen waarin verwijzingen niet zijn toegestaan
- dankzij de
operator T& () const noexcept;
, het kan overal worden gebruikt waar je een verwijzing zou kunnen gebruiken, omdat het er automatisch naar wordt geconverteerd.