Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

std::ref and swap function does not seem to work well together

Tags:

c++

c++11

template <typename T>
void myswap(T a,T b)
{
    T temp = a;
    a = b;
    b = temp;
}
int main()
{
  int m(20),n(30); 
  myswap(ref(m),ref(n));
  //m is still 20 and n is still 30
}

Why have not the values of m and n interchanged? Passing a value wrapped in std::ref to an INCREMENT function results in value change in the original variable (variable in the stack frame that calls INCREMENT function). Or, Is std::ref usage is restricted/limited?

like image 400
DIVAKAR VENKATRAMANI Avatar asked Dec 11 '15 12:12

DIVAKAR VENKATRAMANI


3 Answers

std::ref (and its associated std::reference_wrapper) is a tool designed for very specific use cases in the standard library and should only be used for those; if you want to use it in your own places, you have to understand it in detail and respect what it does.

Fundamentally, a reference_wrapper is much closer to a pointer than a reference. So substitute a pointer in your swap function and you'll see that there's no reason to assume that it would actually swap:

void myswap(int* a, int* b)
{
    int* temp = a;
    a = b;
    b = temp;
}
like image 142
Sebastian Redl Avatar answered Nov 09 '22 22:11

Sebastian Redl


Your code creates two temporary std::reference_wrapper objects and swaps them, so they refer to different objects. All that happens is you swap two reference_wrapper objects, not their targets.

If you manually write what the function template will generate the reason for the behaviour should be obvious:

void myswap(std::reference_wrapper<int> a, std::reference_wrapper<int> b)
{
  std::reference_wrapper<int> temp = a;
  a = b;
  b = a;
}

Obviously this doesn't change the int objects, only the reference_wrapper objects.

If what you're trying to do is force myswap to take references you need to call myswap<int&>(m, n), you can't emulate that by using reference_wrapper. But you should really fix myswap, because it's pretty useless the way it's written now.

like image 34
Jonathan Wakely Avatar answered Nov 10 '22 00:11

Jonathan Wakely


Your myswap takes the elements by value.

Essentially you swap the two references (std::reference_wrapper s) at the local scope of the function.

The values they point to won't change.


template <typename T> void incrementer(T a) { ++a; } int a = 20;

In this case there's a conversion to int& with:

operator T& () const noexcept { return *_ptr; }

In your code, on the other hand:

T temp = a;

will simply call the copy constructor, which copies the underlying pointer:

reference_wrapper(const reference_wrapper&) noexcept = default;

then, on the next lines you again copy the pointer:

reference_wrapper& operator=(const reference_wrapper& x) noexcept = default;
like image 25
Karoly Horvath Avatar answered Nov 09 '22 23:11

Karoly Horvath