将函数转换为引用类型、标准中的漏洞或编译器错误?

Oli*_*liv 7 c++ casting language-lawyer compiler-bug

根据标准,带支撑的函数式转换总是会产生一个纯右值,[expr.cast]/2

否则,表达式是指定类型的纯右值,其结果对象是用初始化程序直接初始化的。

指定类型是引用类型时,这很难解释,因为它可能发生在泛型编程中。在这种情况下,编译器采用了特定的行为:

#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&>);
}
Run Code Online (Sandbox Code Playgroud)

对于 Clang,GCC 和 MSVC 同意decltype(T{a})whereT is A&是 type的事实A&。这意味着根据 decltype 规范,结果不是纯右值。所以看起来这些编译器都不符合标准。

T{a}对于 Clang 和 MSVC的评估只是直接引用绑定。

GCC 拒绝编译g1. 表达式T{a}构造了 的副本,a然后临时绑定到的结果T{a}(这可以在此处模板 h 的显式实例化的程序集中看到)。

在这种情况下,任何编译器都正确吗?还是只是“无需诊断”的情况?

Lan*_*yer 5

这是CWG1521

T{expr} 带引用类型

根据 8.2.3 [expr.type.conv] 第 4 段,

类似地,简单类型说明符类型名称说明后跟花括号初始化列表创建指定类型直接列表初始化(11.6.4 [dcl.init.list])的临时对象,并带有指定的花括号- init-list,其值是作为纯右值的临时对象。

这种措辞不处理 where Tis 引用类型的情况:不可能创建该类型的临时对象,并且结果可能是 xvalue,而不是 prvalue。