9

Assuming I have:

  • class A which is non-copyable
  • class B which has as a member, const A& a (and takes an A in its constructer and sets it in its initialization list)
  • a function A GenerateA();

Does this mean that it should be valid to do: B(GenerateA()) ?

i.e, does the const ref mean that no copy of the A that generateA() returns is done? And does that mean that the scope of the returned temporary is extended for as long as B exists?

EDIT: Addon question from the comments: Is it acceptable to return a A& from GenerateA() to a local A, if the lvalue is a const A&?

Thanks!

please delete me
  • 711
  • 2
  • 9
  • 17
  • You cannot hold a reference to a value who's lifetime has ended; which is exactly what happens when you return a reference to a local variable. – GManNickG Nov 10 '10 at 05:08
  • @GMan: That was essentially the intent of my question, i.e, does using a const reference change the lifetime of said object. – please delete me Nov 10 '10 at 05:10

4 Answers4

4

If A is non-copyable, then the function A GenerateA() is invalid since returning by value requires creating a copy.

If the function returns a reference instead (i.e. A &GenerateA()) and the reference is to a locally created A object, it becomes invalid as soon as the function exits. C++ doesn't have any form of garbage collection, so there is no way to "extend" the lifetime of an object as long as it is in use.

casablanca
  • 69,683
  • 7
  • 133
  • 150
  • Could you elaborate on that? Is the copy required because it has to get the object to the right stackframe? is it acceptable to return a A& from GenerateA() to a local A, if the lvalue is a const A&? Thanks! – please delete me Nov 10 '10 at 04:58
  • 2
    @John Bind: Stack frames are implementation-dependent and do not affect how the language works. The semantics of returning by value is that a copy is created - even though the compiler could optimize this, it is still an error if the object cannot be copied. – casablanca Nov 10 '10 at 05:02
  • @casablanca: Good to know. Thank you. Side point: "compiler could optimize" == RVO? – please delete me Nov 10 '10 at 05:11
  • @John Bind: Even in case of RVO, copy semantics have to be honoured – Chubsdad Nov 10 '10 at 05:46
  • @casablanca : "there is no way to "extend" the lifetime of an object as long as it is in use", that's actually not entirely true, see my answer – icecrime Nov 10 '10 at 07:25
3

As it has already been stated by others, A GenerateA() cannot compile if A is not copyable.

Regarding the const ref : no, the lifetime of the temporary will not be extended to the lifetime of B. The standard [12.2.5] states :

A temporary bound to a reference member in a constructor's ctor-initializer (12.6.2) persists until the constructor exits. [...] A temporary bound to the returned value in a function return statement (6.6.3) persists until the function exits.

So yes, extension of the lifetime of a temporary exists in some contexts (and is sometime truly useful : see this article), but not in the one you presented.

Regarding your last question, it's not legal to return a reference to a local variable from GenerateA() (and binding the result to a const reference won't be of any help).

icecrime
  • 74,451
  • 13
  • 99
  • 111
0

Yes and No.

Yes, the const reference will bind to the temporary variable. No, const references which are class members do not extend lifetime the way const references with automatic duration do.

Ben Voigt
  • 277,958
  • 43
  • 419
  • 720
  • Could you point me to the relevant section in the standard? And, it *doesn't* work. The compiler (should, gcc does) complain about the copy constructed for object A being private. – please delete me Nov 10 '10 at 04:53
  • 2
    @John: that is cause by `A` being returned by copy from `generateA()`, not by the rest of the code. The standard requires an accessible copy constructor (or, eventually, a move constructor) to call `generateA()`. – André Caron Nov 10 '10 at 04:59
0

Here's an example:

#include <iostream>
using namespace std;

int& GenX(bool reset)
{
    static int* x = new int;
    *x = 100;
    if (reset)
    {
        delete x;
        x = new int;
        *x = 200;
    }
    return *x;
}

class YStore
{
public:
    YStore(int& x);
    int& getX() { return my_x; }
private:
    int& my_x;
};

YStore::YStore(int& x)
 : my_x(x)
{
}

int main()
{
    YStore Y(GenX(false));
    cout << "X: " << Y.getX() << endl;
    GenX(true); // side-effect in Y
    cout << "X: " << Y.getX() << endl;
    return 0;
}

Output:

X: 100
X: 200
Will Bickford
  • 5,381
  • 2
  • 30
  • 45
  • 1
    This defeats the purpose of using a reference, and creates a memory leak. – casablanca Nov 10 '10 at 05:05
  • Why does this change x to 200? GenX doesn't return a int*, it returns a int&, which should mean that the second time you call GetX() you should be reading from free'd memory, but, instead you get 200? Is there something about the static that I'm missing? – please delete me Nov 10 '10 at 05:14
  • It has to do with how the reference is bound to the value. When we return a reference to the value we are just returning x in essence. x is a static pointer, so it lives for the lifetime of the program. When we update x with a new memory location we cause anyone who holds a reference to x to be updated at the same time. – Will Bickford Nov 10 '10 at 05:44
  • But you're not returning X, you're returning *x, which should be the de-referenced pointer (so the actual value at location x), which should be free'd after your delete? – please delete me Nov 10 '10 at 07:29
  • 1
    @Will Bickford: To elaborate, I'd expect that behaviour if we were returning x itself, but not *x. I feel I'm missing something obvious, should I open a new question? – please delete me Nov 10 '10 at 07:34
  • It isn't returning a value - it is returning a reference to that value. – Will Bickford Nov 10 '10 at 17:03