1

I'm having a difficult time with understand + use move semantics in C++. I have an object Variable implements move constructor and move assignment but no copy constructor and assignment. Generally copying a Variable makes no sense and I want to prohibit copying explicitly.

class Variable {
public:
    // ctod/dtor things
    Variable(Variable&&);
    Variable& operator =(Variable&&);
    // ...
};

The question is what is correct way of returning a Variable from a function?

Variable& UserObject::giveMeYourVariable() {
    // Calculate parameters
    Variable v(/* Some parameters */);
    return v; // <=== warning: reference to local variable 'v' returned
}

In another code:

UserObject x* = new UserObject;
Variable v = std::move(x->giveMeYourVariable())

Above code compiles with no error but a warning about returning a reference to local variable. Does this code leaks memory or cause undefined behavior or return a deleted reference? What am doing wrong?

Update
Return by value causes error while initializing a reference type (inside a code that is generated by a parser-generator):

Variable& tmp (this->a_function()); <<== error

The error says:

error: invalid initialization of non-const reference of type 'Variable&' \\
from an rvalue of type 'Variable'

Update 2
This issue is reported in XSD mailing list and will be resolved in next release.

sorush-r
  • 9,347
  • 14
  • 77
  • 160
  • 1
    Returning references to locals leads to undefined behaviour, whether you use move semantics or not. [Stop stealing hotel room keys!](http://stackoverflow.com/questions/6441218/can-a-local-variables-memory-be-accessed-outside-its-scope/6445794#6445794) – R. Martinho Fernandes Aug 05 '13 at 09:58
  • I see. So what I gather is that it's not a good idea to implement move constructor without a copy constructor. Right? – sorush-r Aug 05 '13 at 10:07
  • That conclusion makes no sense. Why would you think so? (see `unique_ptr` http://en.cppreference.com/w/cpp/memory/unique_ptr for the textbook example of such a class). – R. Martinho Fernandes Aug 05 '13 at 10:08
  • That parser generator is broken. – R. Martinho Fernandes Aug 05 '13 at 10:12
  • I'm in a situation that have no control over a big part of my project's code that's generated by CodeSynthesis XSD. Somewhere in generated code, it tries to assign a local variable from the reference returned from a member function. I can choose type of returned value. The only solution looks provide a copy constructor. – sorush-r Aug 05 '13 at 10:16
  • That won't work either. The fact that the generated code makes a reference is what needs fixing. – R. Martinho Fernandes Aug 05 '13 at 10:19

2 Answers2

4

Your program invokes undefined behavior.
As the compiler already told you, the lifetime of your Variable object is limited to the function call and the reference you return is no longer valid after the call.

You can simply return your Variable object by value

Variable UserObject::giveMeYourVariable() {

and happily move it around.

mkaes
  • 12,760
  • 9
  • 50
  • 67
  • Returning by value says: `error: invalid initialization of non-const reference of type 'AIT::CSP::Variable&' from an rvalue of type 'AIT::CSP::Variable'` – sorush-r Aug 05 '13 at 10:06
  • 2
    Of course you cannot initialize a reference with the return value. In your example you moved the returned object into a new variable. If you need a reference to your `Variable` you need to store it within your `UserObject` – mkaes Aug 05 '13 at 10:14
  • Use a variable, not a reference, as target – Manu343726 Aug 05 '13 at 10:28
-3

You should move twice and return Variable object by value:

#include <utility>

class Variable {
public:
    // ctod/dtor things
    Variable() {}
    Variable(Variable&&) {}
    Variable& operator =(Variable&&) {return *this;}
    // ...
};

Variable foo() {
    Variable v;
    return std::move(v);
}

int main() {
    Variable v = std::move(foo());
    return 0;
}

std::move just switch type from Variable to Variable&&, that allows to call constructor with move semantic

so:

first std::move allows to create temporary object for return

second std::mode allows to create Variable v

derikon
  • 3
  • 2
  • There is no need to call `std::move` in either case. The first one even inhibits copy elision (NRVO). – juanchopanza Aug 05 '13 at 11:03
  • NRVO works just in simple cases (like my example) but does not work in non-trivial cases (when return value depends of branching: `if(g) return v1; return v2`), does it? – derikon Aug 05 '13 at 11:24
  • So? Why would you want to inhibit it? There is nothing to be gained from returning with `std::move`. – juanchopanza Aug 05 '13 at 11:29
  • Hm. I experimented a little and realized that not completely understand this topic :) I do not understand why I can return object without std::move even if copy constructor is private – derikon Aug 05 '13 at 11:43
  • It is confusing stuff at first. There is a good related SO post [here](http://stackoverflow.com/questions/4986673/c11-rvalues-and-move-semantics-confusion). – juanchopanza Aug 05 '13 at 11:44
  • Thanks for the reference to explanation! – derikon Aug 05 '13 at 11:53