Returning const reference to local variable from a function

26,288

Solution 1

1. Is getA1() implementation correct ? I feel it is incorrect as it is returning address of local variable or temporary.

The only version of getAx() that is correct in your program is getA3(). Both of the others have undefined behaviour no matter how you use them later.

2. Which of the statements in main ( 1,2,3) will lead to undefined behavior ?

In one sense none of them. For 1 and 2 the undefined behaviour is as a result of the bodies of the functions. For the last line, newA3 should be a compile error as you cannot bind a temporary to a non const reference.

3. In const A& newA1 = getA1(); does standard guarantees that temporary bound by a const reference will not be destroyed until the reference goes out of scope?

No. The following is an example of that:

A const & newConstA3 = getA3 ();

Here, getA3() returns a temporary and the lifetime of that temporary is now bound to the object newConstA3. In other words the temporary will exist until newConstA3 goes out of scope.

Solution 2

Q1: Yes, this is a problem, see answer to Q2.

Q2: 1 and 2 are undefined as they refer to local variables on the stack of getA1 and getA2. Those variables go out of scope and are no longer available and worse can be overwritten as the stack is constantly changing. getA3 works since a copy of the return value is created and returned to the caller.

Q3: No such guarantee exists to see answer to Q2.

Solution 3

I think the main problem is that you are not returning temporaries at all, you should

return A(5);

rather than

A a(5);
return a;

Otherwise you are returning local variable address, not temporary. And the temporary to const reference only works for temporaries.

I think its explained here: temporary to const reference

Share:
26,288

Related videos on Youtube

aJ.
Author by

aJ.

I am a C++ developer from INDIA, interested in C, C#.NET, Perl.

Updated on May 10, 2020

Comments

  • aJ.
    aJ. almost 4 years

    I have some questions on returning a reference to a local variable from a function:

    class A {
    public:
        A(int xx)
        : x(xx)
        {
            printf("A::A()\n");
        }
    };
    
    const A& getA1()
    {
        A a(5);
        return a;
    }
    
    A& getA2()
    {
        A a(5);
        return a;
    }
    
    A getA3()
    {
        A a(5);
        return a;
    }
    
    int main()
    {
        const A& newA1 = getA1(); //1
        A& newA2 = getA2(); //2
        A& newA3 = getA3(); //3
    }
    

    My questions are =>

    1. Is the implementation of getA1() correct? I feel it is incorrect as it is returning the address of a local variable or temporary.

    2. Which of the statements in main (1,2,3) will lead to undefined behavior?

    3. In const A& newA1 = getA1(); does the standard guarantee that a temporary bound by a const reference will not be destroyed until the reference goes out of scope?

    • sbk
      sbk over 14 years
      Just to mention that your //3 line shouldn't compile, binding temporary to non-const reference is forbidden by the standard. Visual Studio allows it, but it's wrong, gcc won't forgive you that.
    • aJ.
      aJ. over 14 years
      That's good point @sbk. Its explained in Q2 of this link: herbsutter.wordpress.com/2008/01/01/…
  • Richard Corden
    Richard Corden over 14 years
    I don't think this is right. The concept of temporary doesn't propagate through the return type at all. Consider that you may be calling a function in different translation unit and the caller cannot know if the return expression was a temporary or not.
  • David Rodríguez - dribeas
    David Rodríguez - dribeas over 14 years
    Even if you were returning temporaries, this will fail. Test the following code: 'struct X { ~X(){ std::cout << "~X" << std::endl; } }; X const & f() { return X(); } int main() { X const & tmp = f(); std::cout << "still alive?" << std::endl; }' the program will show that the lifetime of the temporary inside f() does not extend to the lifetime of the tempoary in main().
  • Arkaitz Jimenez
    Arkaitz Jimenez over 14 years
  • Kiran Kumar
    Kiran Kumar over 14 years
    I think the last statement is confusing..you mean the temporary will not be destroyed until newConstA3 goes out of scope?
  • Arkaitz Jimenez
    Arkaitz Jimenez over 14 years
    @dribeas check this: Only one X object is constructed and it is a temporary. struct X { ~X(){ std::cout << "~X" << std::endl; } }; X f() { return X(); } int main() { const X & tmp = f(); std::cout << "still alive?" << std::endl; }
  • Johannes Schaub - litb
    Johannes Schaub - litb over 14 years
    @Arkaitz, however not necessarily only one object. The temporary returned by f() is allowed to be copied before being bound to tmp. In that case, the copied object will be destroyed early, and only the copy will have lengthened lifetime.
  • sbk
    sbk over 14 years
    Try this: MyClass& o = tseadf.myFunc(); cout << "Hello World\n"; cout << o.objID << endl; And anyway, can we move on from VC6 now. Although old doesn't always mean bad, in the case of VC6 it does.
  • Satbir
    Satbir over 14 years
    thank for reply, Regarding VC6 , we are bounded to management people :)
  • David Rodríguez - dribeas
    David Rodríguez - dribeas over 14 years
    @Arkaitz: I think I did not make my point clear, my bad. The problem is with the function signature returning a reference. The fact that the element in the return statement is a temporary or not does not affect the incorrectness of the code in the question.
  • David Rodríguez - dribeas
    David Rodríguez - dribeas over 14 years
    ... now, with the change you made: the function does no longer return a reference, but a value object, then the code becomes correct. The difference being in that the temporary in the calling function will be bound to the reference in the calling function and get it's lifetime extended, while with the initial signature the temporary is in the called function. Think of it: at the point of call, the compiler does not know whether the returned const reference is a reference to a temporary or not.
  • David Rodríguez - dribeas
    David Rodríguez - dribeas over 14 years
    ...if the compiler were to allow the temporary to be bound, it would have to reserve extra space inside the calling activation frame for the real object (not the reference), and it would not even know what the object size is. Consider: 'struct X {}; struct Y : public X { int ch; }; struct Z : public X { double d; }; X const & f(); void g() { X const & xr = f(); }' Now, how can the compiler without knowing the f() definition decide what the real type of the returned object reference is? How could it determine what destructor to call?
  • David Rodríguez - dribeas
    David Rodríguez - dribeas over 14 years
    ...or how much extra stack space must be reserved for the unknown temporary, or even if the returned object is a temporary or not. -- Going back to your example, you are returning a variable, the code is creating a temporary inside the function, copying it to the return variable and then binding it to a reference in the caller function. The fact that the compiler can elide the copy (and thus you see only a destructor call) does not mean that the semantics are not those of a copy. You can check it by declaring the copy constructor of X as private. Your code will not compile.
  • David Rodríguez - dribeas
    David Rodríguez - dribeas over 14 years
    @litb: In fact it is the other way around. It is not that the temporary is allowed to be copied, but rather that the compiler is allowed to elide the copy. The result is the same: different implementations could copy or not, but the compiler must check that the copy can be performed to fulfill the semantic requirements, even if at the end the copy is not performed.
  • Johannes Schaub - litb
    Johannes Schaub - litb over 14 years
    @dribeas i haven't indicated that the compiler is allowed to proceed even if the copy constructor is inaccessible. Still, the compiler is allowed to copy - some in SO say that it isn't (because MSVC seem to behave different and because C++0x doesn't allow the implementation to do a copy anymore), so i have developed the language of "the compiler is allowed to copy" instead of "the compiler may elide the copy". The result is the same, as you say. But thanks for the clarification on my comment, i see i was only communicating half the truth.
  • ashishsony
    ashishsony over 12 years
    @DavidRodríguez-dribeas Awesome explanation! +1
  • Matias
    Matias about 2 years
    Good question @Naveen! Although I know you must've moved on from this by now, for posterity's sake, see this good article on this: stackoverflow.com/questions/1465851/…