根据标准,带支撑的功能强制转换总是导致prvalue,[expr.cast]/2
否则,表达式是指定类型的prvalue,其结果对象是用初始值设定项直接初始化的。
当指定的类型是引用类型时,这很难解释,就像在泛型编程中可能发生的那样。 编译器在这种情况下采用了特定行为:
#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&>);
}
对于Clang,GCC和MSVC同意decltype(T{a})
(其中T是a&
)的类型为a&
。 这意味着根据decltype规范,结果不是prvalue。 所以看起来这些编译器都不符合标准。
对于Clang和MSVC的T{a}
的求值只是一个直接的引用绑定。
GCC拒绝编译G1
。 表达式t{a}
构造了a
的副本,然后暂时绑定到t{a}
的结果(这可以在此处模板h的显式实例化的程序集中看到)。
任何编译器在这种情况下都是正确的吗? 还是仅仅是一个“无需诊断”的病例?
这是CWG1521:
带有引用类型的t{expr}
根据8.2.3[expr.type.conv]第4段,
类似地,simple-type-specifier或typename-specifier后跟一个带括号的init-list创建一个具有指定带括号的init-list的指定类型direct-list initialized(11.6.4[dcl.init.list])的临时对象,其值是作为prvalue的临时对象。
这种措辞不能处理T
是引用类型的情况:不可能创建该类型的临时对象,而且估计结果将是xvalue,而不是PRValue。