C++ Verschil tussen std::ref(T) en T&?

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::refde 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::refof std::reference_wrappernodig als we referenties hebben (d.w.z. T&)?


Antwoord 1, autoriteit 100%

Nou refconstrueert een object van het juiste type reference_wrapperom een ​​verwijzing naar een object te bevatten. Dat betekent wanneer je solliciteert:

auto r = ref(x);

Dit retourneert een reference_wrapperen geen directe verwijzing naar x(dwz T&). Deze reference_wrapper(dwz r) bevat in plaats daarvan T&.

Een reference_wrapperis erg handig wanneer u een referencevan 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 yen xhetzelfde basisadresdelen. Bovendien kan yniet 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:

  1. Een reference_wrapperobject (hier r) kan worden gebruikt om een ​​array van referentieste maken wat niet mogelijk was met T&.

  2. rgedraagt ​​zich eigenlijk als een echte referentie (zie hoe r.get()=70de waarde van yheeft gewijzigd) .

  3. ris niet hetzelfde als T&maar r.get()is dat wel. Dit betekent dat rT&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_wrapperwordt herkend door standaardfaciliteiten om objecten door middel van referentie te kunnen doorgeven in pass-by-value-contexten.

std::bindkan 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 iis opgeslagen (op waarde genomen) in f1op het moment dat deze werd geïnitialiseerd, maar f2heeft een std::reference_wrapperop 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_wrapperaan de andere kant is een C++-object dat een referentie kan bevatten. Als zodanig kun je het in standaard containers gebruiken.

std::refis een standaardfunctie die een std::reference_wrapperretourneert op zijn argument. In hetzelfde idee retourneert std::crefstd::reference_wrappernaar een const-referentie.

Een interessante eigenschap van een std::reference_wrapperis 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.

Other episodes