7

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.

Scooter
  • 6,802
  • 8
  • 41
  • 64
Yogeshwer Sharma
  • 3,647
  • 5
  • 25
  • 27

1 Answers1

18

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;
}
Vaughn Cato
  • 63,448
  • 5
  • 82
  • 132
  • Thanks for your comment. While you added your comment, I also edited the question, and seems like what you said is the problem. Thanks. (Sorry, I cannot upvote your answer though, too few points.) – Yogeshwer Sharma May 04 '13 at 06:29
  • Is there an example where return by rvalue reference is useful? Does not seem to be? :( – Yogeshwer Sharma May 04 '13 at 06:37