物化纯右值成员访问场景下decltype行为不符合预期的技术问询
decltype((A().x))返回double而不是预期的double&&? 先贴一下你的测试代码和输出,方便大家理解场景:
#include <iostream> #include <type_traits> struct A { double x; }; int main() { const A && a1 = A(); std::cout << std::is_same_v<decltype((a1.x)), const double&>; std::cout << std::is_same_v<decltype((std::move(a1).x)), const double&&>; std::cout << std::is_same_v<decltype((A().x)), double>; } // 输出结果:111
你说按照cppreference的规则,A().x应该是xvalue,所以decltype((A().x))得返回double&&,但实测老版本编译器却返回double——这事儿其实是C++标准版本更新带来的规则变化,具体来说:
关键原因:C++17前后的规则差异
在C11和C14里,像A().x这种访问类临时对象(prvalue)的非引用类型成员的表达式,它本身是prvalue(纯右值)。而decltype((expr))的规则里,对prvalue表达式会直接返回它的基础类型(也就是double),不会生成右值引用。
但到了C17,标准对值类别做了调整:当你访问prvalue对象的非引用类型非静态成员时,这个表达式会被归类为xvalue(亡值)。这是因为C17引入了“临时对象物化”的机制——访问prvalue的成员时,会先把这个prvalue变成一个实实在在的临时对象,然后再访问它的成员,此时这个成员访问表达式就成了指向临时对象成员的xvalue,对应的decltype((expr))就会返回double&&。
为啥你的编译器没按预期来?
你测试的gcc7.1、gcc5.2、clang3.8这些版本,要么默认用的是C11/C14标准,要么就是早期的C++17实现还没跟上这个规则:
- gcc5.2根本不支持C++17;
- gcc7.1虽然能开C++17,但对这个值类别规则的支持可能还不完善;
- clang3.8同样只支持C++17的部分特性,这个规则刚好没包含在内。
要是你给这些编译器加上-std=c++17的编译选项,像gcc7.1可能就会返回double&&了,但更老的版本比如gcc5.2还是不行。而在完全支持C17及以后的编译器(比如gcc12、clang15)里,开C17标准后,结果就会符合cppreference的描述。
内容的提问来源于stack exchange,提问作者mkmostafa

