0

I have spend a few hours about rvalue s and lvalue. Here is what I understand

int main()
{
  //.....
  Foo foo = Bar1();
  foo = Bar2();
  //......
}  

Foo Bar1()
{
  //Do something including create foo
  return foo;
}

Foo& Bar2()
{
  //Do something including create foo
  return foo;
}

Under c++03, Bar1() would copy the return object (just before return), and then return the address of the copied object; executing a wasteful copy of an object which is about to be destroyed. Bar2() would return the object created within the function.

Under c++11, Bar1() and Bar2() would essentially be equivalent (and also equivalent to Bar2() of c++03).

Is that right? If not, please elaborate.

Nicol Bolas
  • 449,505
  • 63
  • 781
  • 982
aiao
  • 4,621
  • 3
  • 25
  • 47
  • I answered your question, but I am actually confused what your code is supposed to mean. Are `Bar1`/`Bar2` somehow returning global values of type `Foo` that you declared earlier? But then `Foo foo = Bar1()` would be incorrect... – CygnusX1 Dec 06 '12 at 22:43
  • 1
    I suggest to (at least) add `Foo foo;` at the beginning of each function and name the variables outside `a` and `b` (because you can't define `Foo foo` twice) – leemes Dec 06 '12 at 22:47
  • 3
    You need to clarify your code. At this point it is a total mess, making the question completely meaningless. It is illegal to define `Foo foo` twice. Also, is `foo` returned from the functions the same `foo` that is declared above? (I.e. are you trying to initialize an object with itself?) – AnT stands with Russia Dec 06 '12 at 22:49
  • maybe you meant code something like [this](http://ideone.com/AafG0x) which really shows off the differences in C++03 and C++11, but also compiles? – Mooing Duck Dec 06 '12 at 23:20

5 Answers5

6

They are not the same. Bar2() is UB by both standards. You cannot return object created on stack by reference.

In C++03 Bar1() may take advantage of RVO and nothing will be copied. In C++11 Bar1() will even use RVO or will use a move constructor if RVO is not possible.

Juraj Blaho
  • 13,301
  • 7
  • 50
  • 96
  • Why is `Bar2()` undefined behavior? The object returned seems to have global scope. Actually, there is probably a local `foo` meant to be in each function but omitted in the question. In which case `Bar2()` indeed has undefined behavior. – Dietmar Kühl Dec 06 '12 at 22:42
  • 1
    Where do you see an "object created on stack" in that code? – AnT stands with Russia Dec 06 '12 at 22:42
  • 1
    @AndreyT: I don't see a global `foo` object either. There are two same `foo` variables, so I suspect it is different piece of code. Also Bar2() is used to initialize `foo`, so it is another clue that the lines are not related. – Juraj Blaho Dec 06 '12 at 22:47
  • They are not, sorry about that. I will edit – aiao Dec 06 '12 at 22:59
1

The concept of rvalues and lvalues didn't change from older C++ to C++11. What you describe as "C++03" is what should happen. Some compiler optimizations in some cases can reduce the number of unnecessary copies (including unnecessary copy-constructor calls!), but otherwise it is the same.

What did change is that C++11 introduced a concept of rvalue-reference (T&&).

There are several articles on it that you can google up, for example over here:

http://thbecker.net/articles/rvalue_references/section_01.html

CygnusX1
  • 20,968
  • 5
  • 65
  • 109
1

Bar2() doesn't create any copy in either C++ 2003 or C++ 2011. For Bar1() a copy of foo is created in both C++ 2003 and C++ 2011. The use of rvalue references only applies when you actually do have an rvalue or if you have an lvalue which is about to go away and it is being returned.

Of course, the example happens to be undefined behavior because the foo being return is the foo being initialized. That is, it seems you example is messed up by not stating what foo is meant to be when it is returned. Assuming each function has a local variable foo, Bar2() is undefined behavior according to both standards and Bar1() is somewhat different:

  • If there is a move constructor for Foo, C++ 2011 may use the move constructor while C++ 2003 may use the copy constructor.
  • Whether either the move constructor or the copy constructor is used depends on the rest of the function and the compiler: If all return statements in Bar1() return foo, construction of an extra object will be elided by most compilers.
Dietmar Kühl
  • 150,225
  • 13
  • 225
  • 380
  • "*For Bar1() a copy of foo is created in both C++ 2003 and C++ 2011.*" Not entirely true. It depends on how `foo` is defined. If it's a stack variable, then it will be either elided or moved; if a copy happens, it would only be because movement is not possible. – Nicol Bolas Dec 06 '12 at 23:08
  • @NicolBolas: the original example seemed to have a global variable `foo` which would, indeed be copied. Since then the question was edited to remove this in which case it would, indeed, depend on how `foo` is declared. – Dietmar Kühl Dec 06 '12 at 23:31
0

Bar2() would return the object created within the function.

That's plainly wrong. Bar2() would return reference to some object. (Note: it would be UB if object is created on stack inside Bar2()).

Under c++11, Bar1() and Bar2() would essentially be equivalent (and also equivalent to Bar2() of c++03).

Under C++11 meaning would the same. What you really interested in is a move semantics:

Foo Bar3()
{
  //Do something
  return std::move(foo);
}

This would not execute a copy constructor, but a move constructor - which should be much less resource hungry.

hate-engine
  • 2,300
  • 18
  • 26
0

This article may be interesting for you: Want Speed? Pass by Value.

In C++03, Bar1() may copy the object (but this is optimized away with to the so called copy elision -- see link).

In C++11, essentially nothing has changed. The compiler is allowed to either call Foo's copy constructor or Foo's move constructor or do copy elision. But since even in C++03 the copy would be elided, Bar1() does the same in C++11 and C++03.

Bar2() return just a reference, there is nothing different in C++11. Returning references is can be critical. If foo would be a local value, a reference to a already destroyed variable is returned, never to this.

ipc
  • 8,045
  • 29
  • 33