Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there an elegant way to swap references in C++?

Sometimes classes are referencing other classes. Implementing std::swap() for such classes cannot be straightforward, because it would lead to swapping of original instances instead of references. The code below illustrates this behavior:

#include <iostream>

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

A simple workaround with a union:

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

This is effective, but not handsome. Is there a nicer way to swap two references in C++? Also how does C++ standard explain mixing references and values in one union, considering that in Stroustrup's "The C++ Programming Language" book a 'reference' is defined as an 'alternative name of an object, an alias' (p.189).

like image 768
bkxp Avatar asked Sep 12 '14 09:09

bkxp


2 Answers

The union trick you're using is about as non-portable as code gets. The standard places no requirements whatsoever on how compilers implement references. They can (and most probably do) use pointers under the hood, but this is never guaranteed. Not to mention the fact that sizeof(size_t) and sizeof(T*) aren't required to be equal anyway.

The best answer to your problem is: don't use reference members if you need an assignable/swappable class. Just use a pointer member instead. After all, references are non-reseatable by definition, yet by wanting the class swappable, you want something reseatable. And that's a pointer.

like image 57
Angew is no longer proud of SO Avatar answered Sep 28 '22 19:09

Angew is no longer proud of SO


You may use std::reference_wrapper instead of direct reference (which you cannot swap) or pointer (which may be nullptr). Something like:

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

   int& get() const { return r; }
};

Live example

like image 34
Jarod42 Avatar answered Sep 28 '22 17:09

Jarod42