5
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?

Barry
  • 286,269
  • 29
  • 621
  • 977

3 Answers3

8

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;
}
Sebastian Redl
  • 69,373
  • 8
  • 123
  • 157
  • The first sentence is slightly misleading. Of course `std::ref` is not limited to the standard library (as you also note it after it) – Karoly Horvath Dec 11 '15 at 13:10
4

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.

Jonathan Wakely
  • 166,810
  • 27
  • 341
  • 521
  • Indeed, but should not the temporary be pointing to m and n created in the penultimate stack frame?. Pass a variable wrapped in std::ref to a increment function and you will see the value wud have updated. Ain't a temporary created in increment function? Then, how the change is reflected in original variable. – DIVAKAR VENKATRAMANI Dec 11 '15 at 13:14
  • @Jon: Yes, it DOES take 'by value'. template void incrementer(T a) { ++a; } int a = 20; incrementer(ref(a)); //a is 21 now – DIVAKAR VENKATRAMANI Dec 11 '15 at 13:16
  • `reference_wrapper` doesn't support an increment operator, so the increment function causes a conversion to `int&` and increments the target. The swap function only uses the copy constructor and copy assignment, which `reference_wrapper` _does_ support, so those operations work on the `reference_wrapper` directly, not on the target. – Jonathan Wakely Dec 11 '15 at 13:17
  • 1
    **No it doesn't**. Look at http://www.cplusplus.com/reference/functional/reference_wrapper/ and tell me where you see `operator++` declared. The `++bar` excample cause a conversion to `int&` and increments the `int`. Read my previous comment again. "Incrementing a reference_wrapper" doesn't make sense, so instead it changes the thing it refers to. – Jonathan Wakely Dec 11 '15 at 13:19
1

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;
Karoly Horvath
  • 94,607
  • 11
  • 117
  • 176