提问者:小点点

支撑函数强制转换为引用类型,标准中的漏洞还是编译器的bug?


根据标准,带支撑的功能强制转换总是导致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的显式实例化的程序集中看到)。

任何编译器在这种情况下都是正确的吗? 还是仅仅是一个“无需诊断”的病例?


共1个答案

匿名用户

这是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。