Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Braced functional cast to reference type, a hole in the standard or compilers bug?

According to the standard, a braced functional cast always results in a prvalue, [expr.cast]/2

Otherwise, the expression is a prvalue of the specified type whose result object is direct-initialized with the initializer.

Which is hard to interpret when the specified type is a reference type, as it may happen in generic programming. Compiler have adopted specific behavior in this case:

#include <type_traits>

struct A {
    A ()=default;
    A (const A&);
};

template <class T, class U>
decltype(auto) 
f(U& a) {
    static_assert (std::is_same_v <decltype (T{a}), T>);
    return T{a};
}


#if defined(__clang__) || defined(_MSC_VER)
void g1(A a){
    decltype(auto) v = f<A&>(a); //GCC: error try to bind a prvalue to a non-const lvalue
    static_assert (std::is_same_v <decltype(v), A&>);
}
#endif

void g2(A a){
    decltype(auto) v = f<const A&>(a); //GCC: call the copy constructor of A
                                       //MSVC and Clang: a no op (direct reference binding)
    static_assert (std::is_same_v <decltype(v), const A&>);
}

For Clang, GCC and MSVC agree on the fact the decltype(T{a}) where T is A& is of type A&. Which means the result is not a prvalue according to decltype specification. So it looks that none of these compilers is standard compliant.

The evaluation of T{a} for Clang and MSVC is just a direct reference binding.

GCC refuses to compiles g1. The expression T{a} construct a copy of a and the temporary is then tentatively bound to the result of T{a} (this can be seen in the assembly of the explicit instantiation of template h here).

Is any compiler right in such a situation? Or is it just a "no diagnostic required" case?

like image 352
Oliv Avatar asked Jun 11 '20 13:06

Oliv


1 Answers

This is CWG1521:

T{expr} with reference types

According to 8.2.3 [expr.type.conv] paragraph 4,

Similarly, a simple-type-specifier or typename-specifier followed by a braced-init-list creates a temporary object of the specified type direct-list-initialized (11.6.4 [dcl.init.list]) with the specified braced-init-list, and its value is that temporary object as a prvalue.

This wording does not handle the case where T is a reference type: it is not possible to create a temporary object of that type, and presumably the result would be an xvalue, not a prvalue.

like image 103
Language Lawyer Avatar answered Nov 15 '22 03:11

Language Lawyer