Returning Const Reference to Local Variable from a Function

Returning const reference to local variable from a function

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.

Return const reference to local variable correctly

It doesn't work. That way you simply suppress the warning by making the situation harder to analyze. The behavior is still undefined.

Returning const reference

string("foo") creates an object of type std::string containing the value "foo" locally in the function. This object will be destroyed at the end of the function. So returning the reference to that object will be invalid once the code leaves that function [1]. So in main, you will never have a valid reference to that string. The same would be true if you created a local variable inside foo.

The WHOLE point of returning a reference is that you don't make a copy, and initializing a reference (string &f = foo() is an initialization) will not create a copy of the original object - just another reference to the same object [which is already invalid by the time the code is back in main]. For many things, references can be seen as "a different name for the same thing".

The lifetime of the referred object (in other words, the actual object the "alias name" refers to) should ALWAYS have a lifetime longer than the reference variable (f in this case).

In the case of bar, the code will make a copy as part of the return string("bar");, since you are returning that object without taking its reference - in other words, by copying the object, so that works.

[1] pedantically, while still inside the function, but after the end of the code you have written within the function, in the bit of code the compiler introduced to deal with the destruction of objects created in the function.

return const reference vs temporary object

Even if you assign it to a const reference, the return value is declared as passed by value, that means it'll be copied[1] to outside as a temporary object, and then binded to the const reference. Binding temporary object to a const reference is fine, the object won't be destroyed until getting out of the lifetime of the const reference.

On the other hand, returning reference of a local variable is illegel. The local variable'll be destroyed when the function returned, that means the outside reference will be dangled.

EDIT

My point is that if assigning a const ref to a returned local variable is legal, then shouldn't assigning a const ref to a returned const ref of a local variable be legal as well?

The point is the 1st case is not assigning a const ref to a returned local variable, it's assigning a const ref to a returned temporary variable. (Which might copied from the local variable.)


[1] The copy might be omitted according to RVO technically.

What exactly happens when returning const reference to a local object?

Rules of temporary lifetime extension for each specific context are explicitly spelled out in the language specification. And it says that

12.2 Temporary objects

5 The second context is when a reference is bound to a temporary. [...] A temporary bound to the returned value in a function return statement
(6.6.3) persists until the function exits. [...]

Your temporary object is destroyed at the moment of function exit. That happens before the initialization of the recipient object begins.

You seem to assume that your temporary should somehow live longer than that. Apparently you are trying to apply the rule that says that the temporary should survive until the end of the full expression. But that rule does not apply to temporaries created inside functions. Such temporaries' lifetimes are governed by their own, dedicated rules.

Both your foo and your foo_2 produce undefined behavior, if someone attempts to use the returned reference.

Questions about returning const references

It turns out that your second question answers the first. Yes, static local variables stick around for multiple calls of the function, which is why the reference remains valid.

As for the second, that's correct, the two references would be to the same object. Since they are const, as is the object they reference, this usually isn't a problem.

C++: What does a const reference to the return value of a function mean?

Your code:

const std::string &file_name = it->path().string();

extends the lifetime of the temporary std::string returned by std::filesystem::path::string(). Since you've marked that as const, it can't be moved into file_names, it must be copied. Assuming you want a move, you would write:

auto&& file_name = // ...
file_names.push(std::move(file_name));

Notice that std::queue has a push() overload for r-value references.

Modern C++ provides a lot of opportunities for compilers to optimize, so avoiding questions/"confusion" about dangling references (the auto&& syntax is "new" in C++11) might be a better approach:

auto file_name = // ...
file_names.push(std::move(file_name));

Writing "natural" code that "looks and behaves like the ints" is often a good approach. In the unlikely situation you find that this is really a performance bottleneck, you can revisit; write your code for clarity first.

Returning const reference to temporary behaves differently than local const reference?

Given const Val &foo = test(Val(5));, the temporary Val(5) will be destroyed after the full expression immediately, its lifetime won't be extended to the lifteime of the reference foo. It's not bound to foo directly, but bound to the reference parameter of test.

In reference initialization,

(emphasis mine)

Whenever a reference is bound to a temporary or to a subobject
thereof, the lifetime of the temporary is extended to match the
lifetime of the reference, with the following exceptions:

  • a temporary bound to a reference parameter in a function call exists until the end of the full expression containing that function call: if
    the function returns a reference, which outlives the full expression,
    it becomes a dangling reference.

In general, the lifetime of a temporary cannot be further extended by
"passing it on": a second reference, initialized from the reference to
which the temporary was bound, does not affect its lifetime.



Related Topics



Leave a reply



Submit