This question already has answers here:
About binding a const reference to a sub-object of a temporary
(3 answers)
Closed 6 years ago.
It is known feature of C++ that a const reference extends the life time of the temporary object returned from a function, but is it acceptable to use constant reference to the member of the temporary object returned from the function?
Example:
#include <string>
std::pair<std::string, int> getPair(int n)
{
return {std::to_string(n), n};
}
int main(int, char*[])
{
const int x = 123456;
const auto& str = getPair(x).first;
printf("%d = %s\n", x, str.c_str());
return 0;
}
Output:
123456 = 123456
Yes, this code is perfectly acceptable. The rules, according to the standard are ([class.temporary]):
There are two contexts in which temporaries are destroyed at a
different point than the end of the fullexpression. The first context
is when a default constructor is called to initialize an element of an
array. If the constructor has one or more default arguments, the
destruction of every temporary created in a default argument is
sequenced before the construction of the next array element, if any.
The second context is when a reference is bound to a temporary. The
temporary to which the reference is bound or the temporary that is the
complete object of a subobject to which the reference is bound
persists for the lifetime of the reference...
As you can see the highlighted line makes it clear that binding reference to sub-objects is acceptable, as the complete object has to have its lifetime extended as well.
Note that first does qualify as a subobject [intro.object]:
Objects can contain other objects, called subobjects. A subobject can
be a member subobject (9.2), a base class subobject (Clause 10), or an
array element. An object that is not a subobject of any other object
is called a complete object.
It's well defined.
From the standard: $12.2/6 Temporary objects [class.temporary]:
(emphasis mine)
The temporary to which the reference is bound or the temporary that is
the complete object of a subobject to which the reference is bound
persists for the lifetime of the reference
And about the subobect, $1.8/2 The C++ object model [intro.object]:
(emphasis mine)
Objects can contain other objects, called subobjects. A subobject can
be a member subobject ([class.mem]), a base class subobject (Clause
[class.derived]), or an array element. An object that is not a subobject of any other object is called a complete object.
first is bound to reference and it's the member subobject of std::pair, so the temporary std::pair (i.e. the complete object) 's lifetime will be prolonged, the code should be fine.
For reference only: Clang and GCC say yes, VC says no.
As I mentioned in my comment:
The lifetime of the temporary should be prolonged as long as the
lifetime of its member access (str in this case). That said, you
should be just ok by taking return value by copy. RVO will avoid doing
extra copies.
From the standard, section 12.2.5:
The second context is when a reference is bound to a temporary. The
temporary to which the reference is bound or the temporary that is the
complete object of a subobject to which the reference is bound
persists for the lifetime of the reference except:
— 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 a reference parameter in a function call
(5.2.2) persists until the completion of the full-expression
containing the call.
To stay clear out of any trouble, I would rather do:
auto m_p = getPair(x);
This is as efficient as it can get because of RVO which every compiler must be doing for this case.
This seems addressed in 12.2/4-5:
There are two contexts in which temporaries are destroyed at a
different point than the end of the full expression. The first
context... [stuff dealing with arrays]
The second context is when a reference is bound to a temporary. The
temporary to which the reference is bound or the temporary that is the
complete object of a subobject to which the reference is bound
persists for the lifetime of the reference except:
There are four exceptions dealing with constructor binding of reference members, function calls, functions returning by reference, and references bound in new-initializers.
None of these cases apply, so the temporary is destroyed at the end of the full statement, leaving a dangling reference to a member of the temporary.
Just help the compiler realize it can move from the temporary: const auto str = std::move(getPair(x).first);
Related
Consider the following code:
struct Temp{ int i = 0; };
Temp GetTemp() { return Temp{}; }
int main()
{
for (struct{Temp const & t; int counter;} v = {GetTemp(), 0};
v.counter < 10;
++v.counter)
{
// Is v.t guaranteed to not be a dangling reference?
std::cout << v.t.i << std::endl;
}
}
So GetTemp() returns a temporary object, which then gets assigned to a constant reference variable. However, that constant reference variable is a member of an anonymous local struct. Question: Does the C++ standard guarantee that the lifetime of that temporary gets extended till after the loop terminates?
Considering this question, I would have expected the answer to be no, i.e. that I get a dangling reference in the loop body. However, gcc and clang seem to extend the lifetime (see example on godbolt), and even do not complain with -fsanitize=undefined, which surprised me.
For braced aggregate initialization as in your example, lifetime extension has been guaranteed since C++98 (irrespective of the linkage/visibility properties of the class). This is intuitive, since the reference is directly bound to the temporary, and not via some intermediate ctor parameter, as in the question you've linked. For legalese, see [class.temporary] in the C++14 FD that outlines the lifetime extension contexts.
See also the note here which differentiates braced and parenthetical initialization since C++20:
[Note 7: By contrast with direct-list-initialization, narrowing
conversions ([dcl.init.list]) are permitted, designators are not
permitted, a temporary object bound to a reference does not have its
lifetime extended ([class.temporary]), and there is no brace elision.
[Example 3:
struct A {
int a;
int&& r;
};
int f();
int n = 10;
A a1{1, f()}; // OK, lifetime is extended
A a2(1, f()); // well-formed, but dangling reference
Does a constant reference member variable in an anonymous struct extend the lifetime of a temporary?
Yes, the chain of const references is unbroken. There's is only v and v is alive until the end of the for loop and the lifetime of the referenced Temp is therefore extended until then. The fact that the struct is anonymous has no impact.
class.temporary/4
There are two contexts in which temporaries are destroyed at a different point than the end of the full-expression. The first context is when a default constructor is called to initialize an element of an array. If the constructor has one or more default arguments, the destruction of every temporary created in a default argument is sequenced before the construction of the next array element, if any.
class.temporary/5
The second context is when a reference is bound to a temporary. The temporary to which the reference is bound or the temporary that is the complete object of a subobject to which the reference is bound persists for the lifetime of the reference except:
(5.1) A temporary bound to a reference member in a constructor's ctor-initializer ([class.base.init]) persists until the constructor exits.
(5.2) A temporary bound to a reference parameter in a function call ([expr.call]) persists until the completion of the full-expression containing the call.
(5.3) The lifetime of a temporary bound to the returned value in a function return statement ([stmt.return]) is not extended; the temporary is destroyed at the end of the full-expression in the return statement.
(5.4) A temporary bound to a reference in a new-initializer ([expr.new]) persists until the completion of the full-expression containing the new-initializer.
None of the exceptions to the lifetime extension applies to your case.
Following the well accepted answer to this question Do rvalue references allow dangling references? It would seem that xvalues do not have their lifetime extended when assigned to a rvalue reference lvalue like in the question. However when I do this
#include <iostream>
using namespace std;
class Something {
public:
Something() {
cout << "Something()" << endl;
}
Something(const Something&) {
cout << "Something(const Something&)" << endl;
}
Something(Something&&) {
cout << "Something(Something&&)" << endl;
}
~Something() {
cout << "~Something()" << endl;
}
int a;
};
Something make_something() {
return Something{};
}
int main() {
auto&& something = make_something().a;
return 0;
}
The lifetime of the object returned by a call to make_something is extended, even though make_something().a is an xvalue as per http://en.cppreference.com/w/cpp/language/value_category (the third bullet in the xvalues explanation lists the member access I have above as an xvalue,)
a.m, the member of object expression, where a is an rvalue and m is a
non-static data member of non-reference type;
If value categories do not determine when the lifetime of an rvalue will be extended then what does? I am having a hard time understanding when the lifetime of an rvalue is extended in C++
Lifetime extension doesn't care about value categories. As stated by [class.temporary]/p6:
The temporary to which the reference is bound or the temporary that is the complete object of a subobject to which the reference is bound persists for the lifetime of the reference
Emphasis added.
This says nothing here about the value category of the expression being referenced.
What determines whether a temporary is extended is exactly the above (and a few more rules).
But that does not explain why adding an std::move() around the temporary to which the reference is being assigned does not extend the lifetime
std::move is not a magical, compiler-defined construct in C++. It is a function call, and therefore it behaves no differently from any other C++ function call.
So, if you have std::move(Type()), what does that mean? It means that you will create a temporary, bind it to the parameter of std::move, then call that function, which will return something.
Binding a temporary to a function parameter, as stated in [class.temporary]/p6, means that the lifetime of the temporary is fixed to be the lifetime of the full expression that created it (if not for that rule, then the temporary would have to be destroyed at the end of the function call, since that's the end of the reference's lifetime).
It doesn't matter what the function does, says, or implies. It doesn't matter if the compiler could perhaps inline things and determine that the return value is a reference to an argument that came from a temporary. The lifetime of that temporary is fixed to the expression, not extended.
If value categories do not determine when the lifetime of an rvalue will be extended then what does? I am having a hard time understanding when the lifetime of an rvalue is extended in C++
Note that value categories describe expressions not objects. Value categories ( xvalue, prvalue, or whatever ) won't be extended in any way. Only objects can have a lifetime.
From the n4296 standard draft:
§12.2.1
Temporaries of class type are created in various contexts: binding a reference to a prvalue (8.5.3), returning
a prvalue (6.6.3), a conversion that creates a prvalue (4.1, 5.2.9, 5.2.11, 5.4), throwing an exception (15.1),
and in some initializations (8.5).
and
§12.2.4
There are two contexts in which temporaries are destroyed at a different point than the end of the full-
expression. [...]
The second context is when a reference is bound to a temporary. The temporary to which the reference is
bound or the temporary that is the complete object of a subobject to which the reference is bound persists
for the lifetime of the reference
Note: I didn't quote the first context since it has minor revelance regarding the questions. Italic emphasis added by me.
Thus the value category of the function expression makesomething() is a prvalue creating a temporary of class type, according to the first paragraph cited above.
makesomething().a accesses a temporary, complete subobject. Binding this temporary to a reference leads, according to the second context quoated above, to an extended lifetime.
The lifetime of the subobject a is coupled to the lifetime of the previous created temporary making it an expiring value (xvalue). Without extending its lifetime by binding it to a reference it would be destroyed together with the temporary class object. Thus, in this case, after the ;.
Given:
struct hurg { ... };
hurg get_hurg() { return hurg(); }
hurg&& get_mhurg() { return hurg(); }
My understanding and experimenting shows that the following is not undefined behavior (EDIT: thanks to the answers, it turns out I was wrong and the get_mhurg() examples are undefined behavior):
{
const hurg& a = get_hurg();
hurg&& b = get_hurg();
const hurg& c = get_mhurg();
hurg&& d = get_mhurg();
// do stuff with a, b, c, d
}
// a, b, c, d are now destructed
That is, the lifetime of the temporary hurg object returned by get_hurg() and get_mhurg() is extended until the end of the scope.
However, in the case of (function from here):
template <typename T>
auto id(T&& x) -> decltype(auto) { return decltype(x)(x); }
Using it like:
{
const hurg& x = id(hurg());
// the hurg() 'x' refers to is already destructed
hurg&& y = id(hurg());
// the hurg() 'y' refers to is already destructed
// undefined behavior: use 'x' and 'y'
}
In this case, the lifetime of the hurg is not extended.
What determines when the lifetime of temporaries is extended in general? And, in particular, when is it safe to bind the result of a function to a const lvalue ref or an rvalue ref?
And more specifically, what precisely is happening in the id case?
From [class.temporary]:
There are two contexts in which temporaries are destroyed at a different point than the end of the fullexpression.
The first context is when a default constructor is called to initialize an element of an array [...]
The second context is when a reference is bound to a temporary. The temporary to which the reference is
bound or the temporary that is the complete object of a sub-object to which the reference is bound persists
for the lifetime of the reference except:
(5.1) — A temporary object bound to a reference parameter in a function call (5.2.2) persists until the completion
of the full-expression containing the call.
(5.2) — The lifetime of a temporary bound to the returned value in a function return statement (6.6.3) is not
extended; the temporary is destroyed at the end of the full-expression in the return statement.
(5.3) — A temporary bound to a reference in a new-initializer (5.3.4) persists until the completion of the
full-expression containing the new-initializer.
So two things. First, get_mhurg is undefined behavior. The lifetime of the temporary you're returning is not extended. Second, the temporary passed into id lasts until the end of the full-expression containing the function call, but no further. As with get_mhurg, the temporary is not through-extended. So that would also be undefined behavior.
My understanding and experimenting shows that the following is not
undefined behavior:
Well, your understanding and experimenting is wrong. Tip for the wise- don't experiment with undefined behaviour. For example, your experiment could randomly happen to indicate that it's well-defined behaviour, which is one of the possible outcomes of undefined behaviour.
Using the return value of get_mhurg() is undefined behaviour. You are accessing the temporary hurg() outside the scope in which it was created.
The id situation just goes to show that lifetime extension is fucking dumb.
Specifically, lifetime extension only applies to values directly. It never applies to references of any kind. You must initialize the reference with a value. id does not return a value, so no lifetime extension is applied.
I have become accustomed to the fact that a const reference extends the lifetime of a temporary until the reference goes out of scope:
class X {};
{
X const & x = X();
// Lifetime of x extended to the end of the scope
// in which x is declared because it was declared
// as a *const* reference
}
... And I am also aware that a temporary lives until the end of the expression in which it is created:
// Contrived example to make a point about lifetime of a temporary in an expression
class Y
{
public:
Y() : y(5) {}
Y & operator+=(int const & rhs)
{
y += rhs;
return *this;
}
int foo() { return y; }
private:
int y;
};
// n in the following line of code is 11 and the code is valid
// - the lifetime of the temporary persists to the end of the expression
int n = (Y() += 6).foo();
Assuming I am correct about both of the above, I suspect that it is true that a temporary created in a function argument list will persist for the lifetime of the function call even if it is bound to a non-const reference:
class X {};
void foo(X & x)
{
// x is valid in this function,
// even though the parameter is declared
// as a *non*-const reference - correct?
}
// Valid, I think, even though the function parameter
// is declared as a **non**-const reference
// - because the lifetime of the temporary persists until the expression
// is fully evaluated - right?
foo(X());
I imagine that my experience and understanding is correct - that it is safe to bind the temporary created in the function argument list to the non-const reference parameter.
But I'd like to confirm I am right about this, because I was not able to find this question explicity answered anywhere.
Thanks!
Sure you are right.
The standard:
12.2 Temporary objects [class.temporary]
[...]
There are two contexts in which temporaries are destroyed at a different point than the end of the fullexpression.
The first context is when a default constructor is called to initialize an element of an array. If
the constructor has one or more default arguments, the destruction of every temporary created in a default
argument is sequenced before the construction of the next array element, if any.
The second context is when a reference is bound to a temporary. The temporary to which the reference is bound or the temporary that is the complete object of a subobject to which the reference is bound persists for the lifetime of the reference except:
— 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 a reference parameter in a function call (5.2.2) persists until the completion of the full-expression containing the call.
— The lifetime of a temporary bound to the returned value in a function return statement (6.6.3) is not extended; the temporary is destroyed at the end of the full-expression in the return statement.
— A temporary bound to a reference in a new-initializer (5.3.4) persists until the completion of the
full-expression containing the new-initializer.
Want to ask the question again using one more level of constructors, and objects storing references?
Deduplicator has given the language lawyer answer, here is the implementation detail answer:
If you pass any object by reference, you are effectively passing a pointer to that object. Since the caller is only passing a pointer to the object, the object that is passed by reference must be constructed by the caller.
The callee, on the other hand, only ever sees the pointer that is passed in. It does not see how the caller has constructed the object whose pointer is passed. This object could be a temporary (only in the case of a const reference, because temporaries cannot be passed as non-const references), or a variable with function scope, it could be an object that was already passed to the caller by its caller, it could even be an object allocated with new. As such, it cannot destruct the object because it does not know how.
Consequently, it is up to the caller to clean up the temporary after the callee returns control to the caller - anything that is passed in by reference must live for the entire runtime of the callee.
Note that this entire argument is completely ignorant as to whether the reference is const or not.
I have this code (simplified version):
const int& function( const int& param )
{
return param;
}
const int& reference = function( 10 );
//use reference
I can't quite decide to which extent C++03 Standard $12.2/5 wording
The temporary to which the reference is bound or the temporary that is the complete object to a subobject of which the temporary is bound persists for the lifetime of the reference...
is applicable here.
Is reference variable in the code above valid or dangling? Will the reference in the calling code prolong the lifetime of the temporary passed as the parameter?
A full-expression is an expression that is not a subexpression of another expression. In this case, the full-expression containing the call function( 10 ) is the assignment expression:
const int& reference = function( 10 );
In order to call function with the argument 10, a temporary const-reference object is created to the temporary integer object 10. The lifetime of the temporary integer and the temporary const-reference extend through the assignment, so although the assignment expression is valid, attempting to use the integer referenced by reference is Undefined Behavior as reference no longer references a live object.
The C++11 Standard, I think, clarifies the situation:
The temporary to which the reference is bound or the temporary that is the complete object of a subobject to which the reference is bound persists for the lifetime of the reference except:
...
— A temporary bound to a reference parameter in a function call (5.2.2) persists until the completion of the full-expression containing the call.
"The temporary to which the reference is bound ... persists for the lifetime of the reference". In this case, the lifetime of the reference ends at the end of the assignment expression, as does the lifetime of the temporary integer.
This will compile, but you will end up with a dangling reference. param is freed after function returns.
function is called with a reference to the temporary, anonymous object
function returns the reference
now that function has returned the temporary param is released
the reference is now dangling as the object was destroyed
If you had made it non-const, then it wouldn't have compiled because you cannot pass a non-const reference to an anonymous object.
From the C++11 viepoint a reference returned by a function is not a temporary:
12.12.1 Temporaries of class type are created in various contexts: binding a reference to a prvalue (8.5.3), returning
a prvalue (6.6.3), a conversion that creates a prvalue (4.1, 5.2.9, 5.2.11, 5.4), throwing an exception (15.1),
entering a handler (15.3), and in some initializations (8.5).
A function returning a reference dosn't return prvalue ("pure rvalue"), so it's not a temporary.
This seems quite natural: compiler can't manage lifetime of referenced objects, it is programmer's responsibility
Thus, the compiler doesn't provide any liftime guarantes for const int& reference since it is not bounded to temporary.
It is this part that is important
The temporary to which the reference is bound
In this case the parameter is bound to the temporary, and will be destroyed after the call.
You cannot extend the lifetime further by passing the reference on.