web-dev-qa-db-fra.com

C ++ Différence entre std :: ref (T) et T &?

J'ai quelques questions concernant ce programme:

#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;
}

La sortie est:

false

Je veux savoir si std::ref ne retourne pas la référence d'un objet, que fait-il alors? En gros, quelle est la différence entre:

T x;
auto r = ref(x);

et

T x;
T &y = x;

Aussi, je veux savoir pourquoi cette différence existe? Pourquoi avons-nous besoin std::ref ou std::reference_wrapper lorsque nous avons des références (c.-à-d. T&)?

68
CppNITR

Eh bien ref construit un objet du type reference_wrapper Approprié pour contenir une référence à un objet. Ce qui signifie quand vous postulez:

auto r = ref(x);

Ceci retourne un reference_wrapper Et non une référence directe à x (c'est-à-dire T&). Ce reference_wrapper (C'est-à-dire r) contient plutôt T&.

Un reference_wrapper Est très utile lorsque vous souhaitez émuler un reference d’un objet pouvant être copié (il est à la fois copy-constructible et copy- assignable).

En C++, une fois que vous créez une référence (disons y) à un objet (disons x), alors y et x partagent le même message adresse de base. De plus, y ne peut faire référence à aucun autre objet. De plus, vous ne pouvez pas créer un tableau de références c'est-à-dire qu'un code comme celui-ci déclenchera une erreur:

#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;
}

Cependant c'est légal:

#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
*/

En parlant de votre problème avec cout << is_same<T&,decltype(r)>::value;, la solution est la suivante:

cout << is_same<T&,decltype(r.get())>::value;  // will yield true

Laissez-moi vous montrer un programme:

#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
*/

Nous voyons ici trois choses à savoir:

  1. Un objet reference_wrapper (Ici r) peut être utilisé pour créer un tableau de références, ce qui n’était pas possible avec T&.

  2. r agit en réalité comme une référence réelle (voir comment r.get()=70 a changé la valeur de y).

  3. r n'est pas identique à T& mais r.get() est. Cela signifie que r détient T&, C'est-à-dire comme son nom l'indique est un enveloppe autour d'une référenceT&.

J'espère que cette réponse est plus que suffisante pour expliquer vos doutes.

65
Ankit Acharya

std::reference_wrapper Est reconnu par les installations standard pour pouvoir transmettre des objets par référence dans des contextes de définition de valeur.

Par exemple, std::bind Peut intégrer la std::ref() à quelque chose, la transmettre par valeur et la décompresser ultérieurement dans une référence.

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();
}

Cet extrait génère:

10
20

La valeur de i a été enregistrée (prise par valeur) dans f1 Au moment de son initialisation, mais f2 A conservé un std::reference_wrapper Par valeur, et se comporte donc comme s'il prenait un int&.

42
Quentin

Une référence (T& Ou T&&) Est un élément spécial du langage C++. Il permet de manipuler un objet par référence et a des cas d'utilisation spéciaux dans la langue. Par exemple, vous ne pouvez pas créer de conteneur standard contenant des références: vector<T&> Est mal formé et génère une erreur de compilation.

Un std::reference_wrapper, Par contre, est un objet C++ capable de contenir une référence. En tant que tel, vous pouvez l'utiliser dans des conteneurs standard.

std::ref Est une fonction standard qui renvoie un std::reference_wrapper Sur son argument. Dans la même idée, std::cref Renvoie std::reference_wrapper À une référence const.

Une propriété intéressante d'un std::reference_wrapper, Est qu'il a un operator T& () const noexcept;. Cela signifie que même s'il s'agit d'un objet vrai, il peut être automatiquement converti en la référence qu'il détient. Alors:

  • puisqu'il s'agit d'un objet assignable à la copie, il peut être utilisé dans des conteneurs ou dans d'autres cas où les références ne sont pas autorisées
  • grâce à sa operator T& () const noexcept;, il peut être utilisé partout où vous pourriez utiliser une référence, car il sera automatiquement converti en cette référence.
26
Serge Ballesta