Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How does return by rvalue reference work?

Just when I thought I kind of understand rvalue reference, I ran into this problem. The code is probably unnecessarily long, but the idea is quite simple. There is a main() function, and returnRValueRef() function.

#include <iostream>

#define NV(x) "[" << #x << "=" << (x) << "]"
#define log(x) cout << __FILE__ << ":" << __LINE__ << " " << x << endl

using namespace std;

class AClass {
 public:
  int a_;

  AClass() : a_(0) {
    log("inside default constructor");
  }
  AClass(int aa) : a_(aa) {
    log("inside constructor");
  }
  int getInt() const {
    return a_;
  }
  void setInt(int a) {
    a_ = a;
  }

  AClass(AClass const & other) : a_(other.a_) {
    log("inside copy constructor");
  }

  AClass & operator=(AClass const & rhs) {
    log("inside assignment operator" << "left value" << NV(a_) << "right value" << NV(rhs.a_));
    a_ = rhs.a_;
    return *this;
  }

  AClass & operator=(AClass && rhs) {
    log("inside assignment operator (rvalue ref)" << "left" << NV(a_) << "right" << NV(rhs.a_));
    a_ = rhs.a_;
    return *this;
  }
};

AClass && returnRValueRef() {
  AClass a1(4);
  return move(a1);
}

int main() {
  AClass a;
  a = returnRValueRef();
}

Okay, I would expect this code to first print "inside default constructor" (for a), then "inside constructor" (for a1), and then assignment operator message with rhs.a_ = 4. But the output is

testcpp/return_rvalue_ref.cpp:14 inside default constructor
testcpp/return_rvalue_ref.cpp:17 inside constructor
testcpp/return_rvalue_ref.cpp:39 inside assignment operator (rvalue ref)left[a_=0]right[rhs.a_=0]

Can somebody explain why the last line in the output prints right[rhs.a_=0] instead of right[rhs.a_=4]? I thought the move() just makes lvalue into rvalue without changing its contents. But I am clearly missing something.

Thanks so much for your help. :-)

Edit: I think I know what might be going on. May be the destructor for a1 in function returnRValueRef() is being called when it goes out of scope (even if it is turned into rvalue), and after that the memory location for a1 (or rvalue reference for it) contains undefined stuff! Not sure if that is what is happening, but seems plausible.

like image 337
Yogeshwer Sharma Avatar asked May 04 '13 06:05

Yogeshwer Sharma


People also ask

What is the purpose of an rvalue reference?

Rvalue references is a small technical extension to the C++ language. Rvalue references allow programmers to avoid logically unnecessary copying and to provide perfect forwarding functions. They are primarily meant to aid in the design of higer performance and more robust libraries.

Can a function return an rvalue?

Typically rvalues are temporary objects that exist on the stack as the result of a function call or other operation. Returning a value from a function will turn that value into an rvalue. Once you call return on an object, the name of the object does not exist anymore (it goes out of scope), so it becomes an rvalue.

Can you pass an lvalue to an rvalue reference?

By overloading a function to take a const lvalue reference or an rvalue reference, you can write code that distinguishes between non-modifiable objects (lvalues) and modifiable temporary values (rvalues). You can pass an object to a function that takes an rvalue reference unless the object is marked as const .

What does && mean in C++ functions?

The logical AND operator ( && ) returns true if both operands are true and returns false otherwise. The operands are implicitly converted to type bool before evaluation, and the result is of type bool . Logical AND has left-to-right associativity.


1 Answers

An rvalue reference is still a reference. In your case, you are referencing the local variable that has been destructed. Therefore it is undefined behavior to access the members. What you want to do is to return the object:

AClass returnRValueRef() {
  AClass a1(4);
  return move(a1);
}

However, a move happens automatically with a local variable, so you really only need to do this:

AClass returnRValueRef() {
  AClass a1(4);
  return a1;
}
like image 123
Vaughn Cato Avatar answered Nov 14 '22 18:11

Vaughn Cato