Existe-t-il un moyen élégant d’échanger des références en C ++?

Parfois, les classes font référence à d’autres classes. Implémenter std::swap() pour de telles classes ne peut pas être simple, car cela entraînerait un échange d’instances originales au lieu de références. Le code ci-dessous illustre ce comportement:

 #include  class A { int& r_; public: A(int& v) : r_(v) {} void swap(A& a) { std::swap(r_, a.r_); } }; void test() { int x = 10; int y = 20; A a(x), b(y); a.swap(b); std::cout << "x=" << x << "\n" << "y=" << y << "\n"; } int main() { test(); return 0; } 

Une solution de contournement simple avec un syndicat:

 class A { union { int& r_; size_t t_; }; public: A(int& v) : r_(v) {} void swap(A& a) { std::swap(t_, a.t_); } }; 

C’est efficace, mais pas beau. Existe-t-il un moyen plus agréable d’échanger deux références en C ++? De plus, comment la norme C ++ explique-t-elle le mélange de références et de valeurs dans une union, en considérant que dans le livre “Le langage de programmation C ++” de Stroustrup, une “référence” est définie comme un “nom alternatif d’un object, un alias” (p.189).

Le truc de l’union que vous utilisez est à peu près aussi portable que le code. La norme ne pose aucune exigence quant à la manière dont les compilateurs implémentent les références. Ils peuvent (et très probablement utilisent) des pointeurs sous le capot, mais cela n’est jamais garanti. Sans oublier le fait que sizeof(size_t) et sizeof(T*) ne doivent de toute façon pas être égaux.

La meilleure réponse à votre problème est la suivante: n’utilisez pas de membres de référence si vous avez besoin d’une classe assignable / swappable. Utilisez simplement un membre de pointeur à la place. Après tout, les références ne peuvent pas être réinsérées par définition, mais en voulant que la classe soit échangeable, vous voulez quelque chose de réinscriptible. Et c’est un pointeur.

Vous pouvez utiliser std::reference_wrapper place de la référence directe (que vous ne pouvez pas swap ) ou du pointeur (qui peut être nullptr ). Quelque chose comme:

 class A { std::reference_wrapper r; public: A(int& v) : r(v) {} void swap(A& rhs) { std::swap(r, rhs.r); } int& get() const { return r; } }; 

Exemple en direct

Votre programme utilisant l’ union n’est pas légal, car vous affectez la référence mais permutez l’entier que vous souhaitez utiliser à nouveau. Vous pouvez lire pourquoi, ici: Accès à un membre inactif et à un comportement indéfini?

Une solution simple consiste à utiliser des pointeurs au lieu de références. Ensuite, tout fonctionnera sans heurts, sans code spécial. Si vous n’aimez vraiment pas cela, vous pouvez peut-être utiliser boost::optional place de références simples, mais j’aurais du mal à voir comment cela pourrait être meilleur.

Si je comprends bien votre exemple, ce que vous aimeriez faire est essentiellement de relier les références. Vous ne pouvez pas faire cela en C ++. Vous pouvez échanger les valeurs dans les objects référencés ou utiliser des pointeurs.