Skip to content

CWG2894 [expr.type.conv] T{...} Functional casts create prvalues of reference type T #536

Description

@eisenwave

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 T be 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 type T is cv void and the initializer is the initializer shall be () or {} (after pack expansion, if any), and the expression is a prvalue of type void that performs no initialization.
  • Otherwise, the expression is a prvalue of the specified type whose result object is direct-initialized with the initializer has the same effect as direct-initializing an invented variable T t with the given initializer and then using t as the result of the expression. The result is an lvalue if the specified type T is an lvalue reference type or reference to function type, an xvalue if T is 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.]

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions