CWG2894 [expr.type.conv] T{...} Functional casts create prvalues of reference type T (original) (raw)
Reference: [expr.type.conv] paragraph 2, sentence 3
Issue description
In T{...}, if T is a reference type, [expr.type.conv] paragraph 2, sentence 3 applies, stating:
Otherwise, the expression is a prvalue of the specified type whose result object is direct-initialized with the initializer.
This is defective for references; there can be no prvalues of reference type.
Furthermore, it is not sufficiently clear that void(1, 2) and void{1} are not valid. All compilers reject these forms, and should.
Suggested resolution
Update [expr.type.conv] paragraph 2 as follows:
Let
Tbe the specified type. The effect of the expression is as follows:
- If the initializer is a parenthesized single expression, the type conversion expression is equivalent to the corresponding cast expression.
- Otherwise, if
the typeTis cvvoidand the initializer isthe initializer shall be()or{}(after pack expansion, if any), and the expression is a prvalue of typevoidthat performs no initialization.- Otherwise, the expression
is a prvalue of the specified type whose result object is direct-initialized with the initializerhas the same effect as direct-initializing an invented variableT twith the given initializer and then usingtas the result of the expression. The result is an lvalue if the specified typeTis an lvalue reference type or reference to function type, an xvalue ifTis an rvalue reference to object type, and a prvalue otherwise. If the initializer is a parenthesized optional expression-list, the specified type shall not be an array type.
To [expr.type.conv] paragraph 2, append an example:
void f() { unsigned(-1); // OK, equivalent to (int) -1 unsigned{-1}; // ill-formed, narrowing conversion
void{}; // OK, prvalue of type void
void(1); // OK, equivalent to (void) 1
void{0}; // ill-formed
void(1, 2); // ill-formed
int(1, 2); // ill-formed
struct S { S(int, int); };
S a = S(1, 2); // OK, S(1, 2) is a prvalue
S b = S(a); // OK, equivalent to S b = (S) a;
using R = S&;
R r = R(a); // OK, equivalent to R r = (R) a;
R q = R{a}; // OK, same}
[Editor's note: The example is intended to be educational and highlight the cases void(1, 2), R{a}, which currently have wording issues or related compiler bugs.]