The C++11 and C++14 standard (and working draft, respectively) say in §3.10.1:
A prvalue (“pure” rvalue) is an rvalue that is not an xvalue. [Example: The result of calling a function
whose return type is not a reference is a prvalue. The value of a literal such as 12, 7.3e5, or true is
also a prvalue. —end example ]
and
An rvalue (so called, historically, because rvalues could appear on the right-hand side of an assignment
expression) is an xvalue, a temporary object (12.2) or subobject thereof, or a value that is not associated
with an object.
Which leads me to the question: How can an expression be "a value not associated with an object"?
I was under the impression, that it is the purpose of expressions to return objects or void (which I do not expect to be a value either).
Is there some simple and common example for such expressions?
Edit 1
To further complicate things, consider the following:
int const& x = 3;
int&& y = 4;
In context of §8.3.2.5, which contains the most interesting snippet:
[...] A reference shall be initialized to refer to a valid object or
function [...]
Which is reinforced by §8.5.3.1:
A variable declared to be a T& or T&&, that is, “reference to type T” (8.3.2), shall be initialized by an object,
or function, of type T or by an object that can be converted into a T. [...]
[intro.object]:
The constructs in a C++ program create, destroy, refer to, access, and manipulate objects. An object is a region of storage. [ Note: A function is not an object, regardless of whether or not it occupies storage in the way that objects do. —end note ] An object is created by a definition (3.1), by a new-expression (5.3.4) or by the implementation (12.2) when needed.
So "a value not associated with an object" is something created not by definition or with new-expression, which also means that it doesn't have corresponding region of storage, like for example a literal.
Edit: Except string literals (see comments)
Examples for such values are all non-array, non-class non-temporary prvalues (a temporary prvalue corresponds to a temporary object). Examples include 2.0 and 1. Counterexamples include "hello" (which is an array), std::string("haha") (which is a class object) or the float prvalue temporary initialized from 2 that is bound to the reference in (const float&){2} (the reference itself is an lvalue!). I think that this simple rule covers the rules accurately.
A C++ Standard's footnote on the lvalue to rvalue conversion says (a little bit outdated, because it was not amended to mention array types)
In C ++ class prvalues can have cv-qualified types (because they are objects). This differs from ISO C, in which non-lvalues never have cv-qualified types.
So the deeper reason that decltype((const int)0) still is type int is that it does not refer to an object. So because there is no object, there is nothing to make const, and consequently the expression will never be const either.
This quote is not as precisely worded as it could be:
An rvalue (so called, historically, because rvalues could appear on the right-hand side of an assignment expression) is an xvalue, a temporary object (12.2) or subobject thereof, or a value that is not associated with an object.
An rvalue is an expression , so it cannot be an object (temporary or otherwise). The intent of the section of this quote talking about temporary objects is to say that value resulting from evaluating the rvalue is a temporary object, and so on.
This is a common shortcut, e.g. with int x; we would casually say "x is in int" , when in fact x is an identifier; and the expression x has type int and designates an int.
Anyway, it divides possible rvalues up into three categories:
xvalue
temporary object
value not associated with an object
The definition of temporary object includes being an object of class type, so it seems to me that "value not associated with an object" should be any non-xvalue of non-class type. For example 1 + 1.
Related
I am trying to understand how reference initialization. For example, let's look at a typical example.
double val = 4.55;
const int &ref = val;
I can think of 2 possibilities of what is happening in the above snippet.
Possibility 1
The usual explanation is given as follows:
Here a temporary(prvalue) of type int with value 4 is created and then the reference ref is bound to this temporary(prvalue) int object instead of binding to the variable val directly. This happens because the type of the variable val on the right hand side is double while on the left hand side we have a reference to int. But for binding a reference to a variable the types should match. Moreover, the lifetime of the temporary prvalue is extended.
Possibility 2
I think there is another possibility that could happen which is as follows:
Here a temporary(prvalue) of type int with value 4 is created. But since const int &ref expects a glvalue and currently we've a prvalue, the temporary materialization kicks in and so the prvalue is converted to an xvalue. Then the reference ref is bound to this materialized xvalue(since xvalue is also a glvalue) instead of binding to the variable val directly. This happens because the type of the variable val on the right hand side is double while on the left hand side we have a reference to int. But for binding a reference to a variable the types should match. Moreover, the lifetime of the materialized temporary xvalue is extended.
My questions are:
Which of the above explanation is correct according to the C++11 standard. I am open to accept that none of the explanation above is correct in which case what is the correct explanation.
Which of the above explanation is correct according to the C++17 standard. I am open to accept that none of the explanation above is correct in which case what is the correct explanation.
I am also confused to whether a prvalue in the first step of both of the possibilities above, is actually a temporary object? Or the xvalue is the actual object. I mean do we have 2 temporary objects, like the first one due to "conversion to prvalue" and second one due to the "prvalue to xvalue" conversion(temporary materiliazation). Or do we only have one temporary which is due to the "prvalue to xvalue" temporary materialization.
PS: I am not looking for a way to solve this. For example, i know that i can simply write:
const double &ref = val;. My aim is to understand what is happening according to C++11 and C++17 standards.
val in const int &ref = val; is a lvalue, not a prvalue. Even if it is converted to a prvalue, this doesn't mean creation of a temporary in either C++11 or C++17. In C++17 this isn't the case since only prvalue-to-xvalue conversion (and some special cases not relevant here) creates a temporary, while in C++11 [conv.lval]/2 says that only for class types lvalue-to-rvalue conversion creates a temporary.
The initialization of a reference is explained in [dcl.init.ref].
In C++11, all previous cases fall through and so according to [dcl.init.ref]/5.2.2 a temporary of the destination type is created and initialized by copy-initialization from the initializer expression and the reference is bound to that temporary. In this copy-initialization the lvalue val is converted to a prvalue of type double and then to a prvalue of type int, neither of these steps creating additional temporaries.
In C++17, all cases fall through until [dcl.init.ref]/5.2.2, which states that the initializer expression is first implicitly converted to a prvalue of the destination type, which does not imply creation of a temporary, and then the temporary materialization conversion (prvalue-to-xvalue conversion) is applied and the reference bound to the result, i.e. the xvalue referring to the temporary.
In the end there is always exactly one temporary, the one created according to the rule in [dcl.init.ref].
Lets look at each of the cases(C++11 vs C++17) separately.
C++11
From decl.init.ref 5.2.2:
Otherwise, a temporary of type “ cv1 T1” is created and initialized from the initializer expression using the rules for a non-reference copy-initialization ([dcl.init]). The reference is then bound to the temporary.
One more important thing to note is that from basic.lval#4:
Class prvalues can have cv-qualified types; non-class prvalues always have cv-unqualified types...
When applied to your example, this means that a temporary of type int is created and is initialized from the initializer expression val using the rules for a non-reference copy-initialization. The temporary int so created has the value category of prvalue if/when used as an expression.
Next, the reference ref is then bound to the temporary int created which has value 4. Thus,
double val = 4.55;
const int &ref = val; // ref refers to temporary with value 4
C++17
From decl.init.ref 5.2.2.2:
Otherwise, the initializer expression is implicitly converted to a prvalue of type “cv1 T1”. The temporary materialization conversion is applied and the reference is bound to the result.
When applied to your example, this means that the initializer expression val is implicitly converted to a prvalue of type const int. Now we currently have a prvalue const int. But before temporary materialization is applied, expr 6 kicks in which says:
If a prvalue initially has the type “cv T”, where T is a cv-unqualified non-class, non-array type, the type of the expression is adjusted to T prior to any further analysis.
This means before temporary materialization could happen, the prvalue const int is adjusted to prvalue int.
Finally, temporary materialization is applied and and ref is bound to the resulting xvalue.
Here's my take for C++17:
double val = 4.55;
const int &ref = val;
We're binding a const int reference to the lvalue expression of type double denoted by val. According to the declaration of references;
Otherwise, the initializer expression is implicitly converted to a prvalue of type “T1”.
we convert the expression val to type int. Note that this implicit type! conversion fits with the draft.
Next,
The temporary materialization conversion is applied, considering the type of the prvalue to be “cv1 T1”, ...
in order to apply the temporary materialization (also known as prvalue -> xvalue conversion), we must have fully matched types so the expression is aditionally converted to const int is considered to be const int without any conversions (source). The value category remains the same (prvalue). And finally,
... and the reference is bound to the result.
we have a reference binding of type T to a prvalue of type T which induces temporary materialization (source) and val gets converted to xvalue. The created temporary object is of type const int and value of 4.
EDIT: And to answer your questions, of course. So neither of the given possibilities aren't technically correct for C++17 right at the start. Strictly speaking, neither prvalues nor xvalues are temporary objects. Rather, they are value category of expressions and expression might (or might not) denote a (temporary) object. So technically, xvalues denote temporary objects while prvalues don't. You could still say that xvalues are temporary objects, that's completely fine by me as long as you know what you're talking about.
With code like
#include <stdio.h>
struct P2d {
double x, y;
P2d(double x, double y) : x(x), y(y) {}
~P2d() { printf("Destructor called\n"); }
};
P2d center() {
return P2d(10, 10);
}
int main(int argc, const char *argv[]) {
const double& x = center().x;
printf("x = %.18g\n", x);
return 0;
}
g++ (version 5.2.0) will destroy the P2d temporary instance before entering the printf in main, but the value will be preserved anyway (i.e. instead of binding x to the actual member of the temporary P2d instance it will create another temporary double to copy the value of the member).
clang++ (IMO correctly) instead extends the lifetime of the temporary P2d instance to the lifetime of the x reference and the destructor will be therefore called after the printf in main.
If instead of using plain double as type for the x and y members you make a class (e.g. Double) then both compilers agree and they extend the lifetime of the temporary P2d object to past the printf.
Is this a bug in g++ or something allowed by the standard?
This is covered by CWG 1651:
The resolution of issues 616 and 1213, making the result of a member
access or subscript expression applied to a prvalue an xvalue, means
that binding a reference to such a subobject of a temporary does not
extend the temporary's lifetime. 12.2 [class.temporary] should be
revised to ensure that it does.
The status quo is that only prvalues are treated as referring to temporaries - thus [class.temporary]/5 ("The second context is when a reference is bound to a temporary.") is not considered applicable. Clang and GCC have not actually implemented issue 616's resolution, though. center().x is treated as a prvalue by both. My best guess:
GCC simply didn't react to any DRs yet, at all. It doesn't extend lifetime when using scalar subobjects, because those are not covered by [dcl.init.ref]/(5.2.1.1)†. So the complete temporary object doesn't need to live on (see aschelper's answer), and it doesn't, because the reference doesn't bind directly. If the subobject is of class or array type, the reference binds directly, and GCC extends the temporary's lifetime. This has been noted in DR 60297.
Clang recognizes member access and implemented the "new" lifetime extension rules already - it even handles casts. Technically speaking, this is not consistent with the way it handles value categories. However, it is more sensible and will be the correct behavior once the aforementioned DR is resolved.
I'd therefore say that GCC is correct by current wording, but current wording is defective and vague, and Clang already implemented the pending resolution to DR 1651, which is N3918. This paper covers the example very clearly:
If E1 is a temporary expression and E2 does not designate a
bit-field, then E1.E2 is a temporary expression.
center() is a temporary expression as per the paper's wording for [expr.call]/11. Thus its modified wording in the aforementioned [class.temporary] /5 applies:
The second context is when a reference does not bind directly (8.5.3
dcl.init.ref) or is initialized with a temporary expression (clause 5). The corresponding temporary
object (if any) persists for the lifetime of the reference except: [...inapplicable exceptions...]
Voilà, we have lifetime extension. Note that "the corresponding temporary object" is not clear enough, one of the reasons for the proposal's deferment; it will assuredly be adopted once it gets revised.
†
is an xvalue (but not a bit-field), class prvalue, array prvalue or function lvalue and “cv1 T1” is reference-compatible with “cv2 T2”, or […]
Indeed, GCC respects this fully and will extend lifetime if the subobject has array type.
I would argue for a bug in g++, because, quoting draft N3242, §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:
So its lifetime must be extended, except when:
A temporary bound to a reference member in a constructor’s ctor-initializer [..]
A temporary bound to a reference parameter in a function call [..]
The lifetime of a temporary bound to the returned value in a function return statement [..]
A temporary bound to a reference in a new-initializer [..]
Our case doesn't fit any of these exceptions, thus it must follow the rule. I'd say g++ is wrong here.
Then, regarding the quote aschepler brought up from the same draft §8.5.3/5 (emphasis mine):
A reference to type "cv1 T1" is initialized by an expression of type "cv2 T2" as follows:
If the reference is an lvalue reference and the initializer expression
a. is an lvalue (but is not a bit-field) and "cv1 T1" is reference-compatible with "cv2 T2", or
b. has a class type ...
then ...
Otherwise, the reference shall be an lvalue reference to a non-volatile const type (i.e., cv1 shall be const), or the reference shall be an rvalue reference.
a. If the initializer expression
i. is an xvalue, class prvalue, array prvalue or function lvalue and "cv1 T1" is reference-compatible with "cv2 T2", or
ii. has a class type ...
then the reference is bound to the value of the initializer expression in the first case....
b. Otherwise, a temporary of type "cv1 T1" is created and initialized from the initializer expression using the rules for a non-reference copy-initialization (8.5). The reference is then bound to the temporary.
Looking at what an xvalue is, this time quoting http://en.cppreference.com/w/cpp/language/value_category ...
An xvalue ("expiring value") expression is [..]
a.m, the member of object expression, where a is an rvalue and m is a non-static data member of non-reference type;
... the expression center().x should be an xvalue, thus case 2a from §8.5.3/5 applies (and not the copy). I'll stay with my suggestion: g++ is wrong.
Just read Columbo's answer.
This is a gcc bug. The relevant rule is in [class.temporary]:
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 except:
— 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.
— 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.
We're binding a reference to a subobject of a temporary, so the temporary should persist for the lifetime of the reference. None of those three exceptions to this rule apply here.
I wanted a clarification regarding C++ value categories.
struct Foo {...};
void do_something(Foo{});
Is the Foo{} above a r-value or an x-value?
I understand that there is a hierarchy of value categories, and that r-values are actually either an x-value or a pr-value. I also know that the standard says that "temporary materialization is an x-value" but I wasn't sure if the creation of a temporary fits under this definition.
But what I wasn't sure about was whether gl-value and r-value were "abstract" categories in the hierarchy, with the leafs (l-value, x-value, and pr-value) being the actual implementations.
Could someone explain this for me?
From https://eel.is/c++draft/basic.lval (accessed 04/02/2021):
Every expression belongs to exactly one of the fundamental
classifications in this taxonomy: lvalue, xvalue, or prvalue. This
property of an expression is called its value category.
This answers your question of whether gl-value and r-value are categories of values: they are.
On what kind of value your particular case is, let's look at xvalue:
An xvalue is a glvalue that denotes an object whose resources can be reused (usually because it is near the end of its lifetime).
[ ... snip ... ]
# [Note 3: An expression is an xvalue if it is:
(4.1) the result of calling a function, whether implicitly or explicitly, whose return
type is an rvalue reference to object type ([expr.call]),
(4.2) a cast to an rvalue reference to object type ([expr.type.conv],
[expr.dynamic.cast], [expr.static.cast] [expr.reinterpret.cast],
[expr.const.cast], [expr.cast]),
(4.3) a subscripting operation with
an xvalue array operand ([expr.sub]),
(4.4) a class member access
expression designating a non-static data member of non-reference type
in which the object expression is an xvalue ([expr.ref]), or
(4.5) a
.* pointer-to-member expression in which the first operand is an
xvalue and the second operand is a pointer to data member
([expr.mptr.oper]).
Your case does not appear to fit any of those. Now let's look at prvalue:
A prvalue is an expression whose evaluation initializes an object or computes the value of an operand of an operator, as specified by the context in which it appears, or an expression that has type cv void.
Your case appears to be initialising an object. For this reason, I would say it's a prvalue.
With code like
#include <stdio.h>
struct P2d {
double x, y;
P2d(double x, double y) : x(x), y(y) {}
~P2d() { printf("Destructor called\n"); }
};
P2d center() {
return P2d(10, 10);
}
int main(int argc, const char *argv[]) {
const double& x = center().x;
printf("x = %.18g\n", x);
return 0;
}
g++ (version 5.2.0) will destroy the P2d temporary instance before entering the printf in main, but the value will be preserved anyway (i.e. instead of binding x to the actual member of the temporary P2d instance it will create another temporary double to copy the value of the member).
clang++ (IMO correctly) instead extends the lifetime of the temporary P2d instance to the lifetime of the x reference and the destructor will be therefore called after the printf in main.
If instead of using plain double as type for the x and y members you make a class (e.g. Double) then both compilers agree and they extend the lifetime of the temporary P2d object to past the printf.
Is this a bug in g++ or something allowed by the standard?
This is covered by CWG 1651:
The resolution of issues 616 and 1213, making the result of a member
access or subscript expression applied to a prvalue an xvalue, means
that binding a reference to such a subobject of a temporary does not
extend the temporary's lifetime. 12.2 [class.temporary] should be
revised to ensure that it does.
The status quo is that only prvalues are treated as referring to temporaries - thus [class.temporary]/5 ("The second context is when a reference is bound to a temporary.") is not considered applicable. Clang and GCC have not actually implemented issue 616's resolution, though. center().x is treated as a prvalue by both. My best guess:
GCC simply didn't react to any DRs yet, at all. It doesn't extend lifetime when using scalar subobjects, because those are not covered by [dcl.init.ref]/(5.2.1.1)†. So the complete temporary object doesn't need to live on (see aschelper's answer), and it doesn't, because the reference doesn't bind directly. If the subobject is of class or array type, the reference binds directly, and GCC extends the temporary's lifetime. This has been noted in DR 60297.
Clang recognizes member access and implemented the "new" lifetime extension rules already - it even handles casts. Technically speaking, this is not consistent with the way it handles value categories. However, it is more sensible and will be the correct behavior once the aforementioned DR is resolved.
I'd therefore say that GCC is correct by current wording, but current wording is defective and vague, and Clang already implemented the pending resolution to DR 1651, which is N3918. This paper covers the example very clearly:
If E1 is a temporary expression and E2 does not designate a
bit-field, then E1.E2 is a temporary expression.
center() is a temporary expression as per the paper's wording for [expr.call]/11. Thus its modified wording in the aforementioned [class.temporary] /5 applies:
The second context is when a reference does not bind directly (8.5.3
dcl.init.ref) or is initialized with a temporary expression (clause 5). The corresponding temporary
object (if any) persists for the lifetime of the reference except: [...inapplicable exceptions...]
Voilà, we have lifetime extension. Note that "the corresponding temporary object" is not clear enough, one of the reasons for the proposal's deferment; it will assuredly be adopted once it gets revised.
†
is an xvalue (but not a bit-field), class prvalue, array prvalue or function lvalue and “cv1 T1” is reference-compatible with “cv2 T2”, or […]
Indeed, GCC respects this fully and will extend lifetime if the subobject has array type.
I would argue for a bug in g++, because, quoting draft N3242, §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:
So its lifetime must be extended, except when:
A temporary bound to a reference member in a constructor’s ctor-initializer [..]
A temporary bound to a reference parameter in a function call [..]
The lifetime of a temporary bound to the returned value in a function return statement [..]
A temporary bound to a reference in a new-initializer [..]
Our case doesn't fit any of these exceptions, thus it must follow the rule. I'd say g++ is wrong here.
Then, regarding the quote aschepler brought up from the same draft §8.5.3/5 (emphasis mine):
A reference to type "cv1 T1" is initialized by an expression of type "cv2 T2" as follows:
If the reference is an lvalue reference and the initializer expression
a. is an lvalue (but is not a bit-field) and "cv1 T1" is reference-compatible with "cv2 T2", or
b. has a class type ...
then ...
Otherwise, the reference shall be an lvalue reference to a non-volatile const type (i.e., cv1 shall be const), or the reference shall be an rvalue reference.
a. If the initializer expression
i. is an xvalue, class prvalue, array prvalue or function lvalue and "cv1 T1" is reference-compatible with "cv2 T2", or
ii. has a class type ...
then the reference is bound to the value of the initializer expression in the first case....
b. Otherwise, a temporary of type "cv1 T1" is created and initialized from the initializer expression using the rules for a non-reference copy-initialization (8.5). The reference is then bound to the temporary.
Looking at what an xvalue is, this time quoting http://en.cppreference.com/w/cpp/language/value_category ...
An xvalue ("expiring value") expression is [..]
a.m, the member of object expression, where a is an rvalue and m is a non-static data member of non-reference type;
... the expression center().x should be an xvalue, thus case 2a from §8.5.3/5 applies (and not the copy). I'll stay with my suggestion: g++ is wrong.
Just read Columbo's answer.
This is a gcc bug. The relevant rule is in [class.temporary]:
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 except:
— 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.
— 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.
We're binding a reference to a subobject of a temporary, so the temporary should persist for the lifetime of the reference. None of those three exceptions to this rule apply here.
With code like
#include <stdio.h>
struct P2d {
double x, y;
P2d(double x, double y) : x(x), y(y) {}
~P2d() { printf("Destructor called\n"); }
};
P2d center() {
return P2d(10, 10);
}
int main(int argc, const char *argv[]) {
const double& x = center().x;
printf("x = %.18g\n", x);
return 0;
}
g++ (version 5.2.0) will destroy the P2d temporary instance before entering the printf in main, but the value will be preserved anyway (i.e. instead of binding x to the actual member of the temporary P2d instance it will create another temporary double to copy the value of the member).
clang++ (IMO correctly) instead extends the lifetime of the temporary P2d instance to the lifetime of the x reference and the destructor will be therefore called after the printf in main.
If instead of using plain double as type for the x and y members you make a class (e.g. Double) then both compilers agree and they extend the lifetime of the temporary P2d object to past the printf.
Is this a bug in g++ or something allowed by the standard?
This is covered by CWG 1651:
The resolution of issues 616 and 1213, making the result of a member
access or subscript expression applied to a prvalue an xvalue, means
that binding a reference to such a subobject of a temporary does not
extend the temporary's lifetime. 12.2 [class.temporary] should be
revised to ensure that it does.
The status quo is that only prvalues are treated as referring to temporaries - thus [class.temporary]/5 ("The second context is when a reference is bound to a temporary.") is not considered applicable. Clang and GCC have not actually implemented issue 616's resolution, though. center().x is treated as a prvalue by both. My best guess:
GCC simply didn't react to any DRs yet, at all. It doesn't extend lifetime when using scalar subobjects, because those are not covered by [dcl.init.ref]/(5.2.1.1)†. So the complete temporary object doesn't need to live on (see aschelper's answer), and it doesn't, because the reference doesn't bind directly. If the subobject is of class or array type, the reference binds directly, and GCC extends the temporary's lifetime. This has been noted in DR 60297.
Clang recognizes member access and implemented the "new" lifetime extension rules already - it even handles casts. Technically speaking, this is not consistent with the way it handles value categories. However, it is more sensible and will be the correct behavior once the aforementioned DR is resolved.
I'd therefore say that GCC is correct by current wording, but current wording is defective and vague, and Clang already implemented the pending resolution to DR 1651, which is N3918. This paper covers the example very clearly:
If E1 is a temporary expression and E2 does not designate a
bit-field, then E1.E2 is a temporary expression.
center() is a temporary expression as per the paper's wording for [expr.call]/11. Thus its modified wording in the aforementioned [class.temporary] /5 applies:
The second context is when a reference does not bind directly (8.5.3
dcl.init.ref) or is initialized with a temporary expression (clause 5). The corresponding temporary
object (if any) persists for the lifetime of the reference except: [...inapplicable exceptions...]
Voilà, we have lifetime extension. Note that "the corresponding temporary object" is not clear enough, one of the reasons for the proposal's deferment; it will assuredly be adopted once it gets revised.
†
is an xvalue (but not a bit-field), class prvalue, array prvalue or function lvalue and “cv1 T1” is reference-compatible with “cv2 T2”, or […]
Indeed, GCC respects this fully and will extend lifetime if the subobject has array type.
I would argue for a bug in g++, because, quoting draft N3242, §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:
So its lifetime must be extended, except when:
A temporary bound to a reference member in a constructor’s ctor-initializer [..]
A temporary bound to a reference parameter in a function call [..]
The lifetime of a temporary bound to the returned value in a function return statement [..]
A temporary bound to a reference in a new-initializer [..]
Our case doesn't fit any of these exceptions, thus it must follow the rule. I'd say g++ is wrong here.
Then, regarding the quote aschepler brought up from the same draft §8.5.3/5 (emphasis mine):
A reference to type "cv1 T1" is initialized by an expression of type "cv2 T2" as follows:
If the reference is an lvalue reference and the initializer expression
a. is an lvalue (but is not a bit-field) and "cv1 T1" is reference-compatible with "cv2 T2", or
b. has a class type ...
then ...
Otherwise, the reference shall be an lvalue reference to a non-volatile const type (i.e., cv1 shall be const), or the reference shall be an rvalue reference.
a. If the initializer expression
i. is an xvalue, class prvalue, array prvalue or function lvalue and "cv1 T1" is reference-compatible with "cv2 T2", or
ii. has a class type ...
then the reference is bound to the value of the initializer expression in the first case....
b. Otherwise, a temporary of type "cv1 T1" is created and initialized from the initializer expression using the rules for a non-reference copy-initialization (8.5). The reference is then bound to the temporary.
Looking at what an xvalue is, this time quoting http://en.cppreference.com/w/cpp/language/value_category ...
An xvalue ("expiring value") expression is [..]
a.m, the member of object expression, where a is an rvalue and m is a non-static data member of non-reference type;
... the expression center().x should be an xvalue, thus case 2a from §8.5.3/5 applies (and not the copy). I'll stay with my suggestion: g++ is wrong.
Just read Columbo's answer.
This is a gcc bug. The relevant rule is in [class.temporary]:
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 except:
— 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.
— 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.
We're binding a reference to a subobject of a temporary, so the temporary should persist for the lifetime of the reference. None of those three exceptions to this rule apply here.