C++17临时对象值类别疑惑:为何operator=返回临时是prvalue?
解析:为何operator=返回的临时对象是prvalue而非xvalue
首先明确几个核心概念的关键区分:
- prvalue(纯右值):表示值的表达式,本身不对应内存中的对象,仅在需要时通过临时物化生成临时对象。
- xvalue(将亡值):标识其资源可被移动的对象的表达式,通常是已物化的临时对象或
std::move处理后的对象。 - 临时对象是prvalue物化后的产物,但prvalue本身不是对象;xvalue是指向该临时对象的表达式类别。
1. 非引用返回的函数调用表达式天然是prvalue
你的operator=返回类型是A(值类型,非引用),根据C++标准,返回非引用类型的函数调用表达式属于prvalue。无论函数内部返回的是*this(lvalue)还是其他值,当返回值为值类型时,编译器会将返回的对象拷贝(或移动,这里因*this是lvalue触发拷贝构造)为一个prvalue表达式,而非xvalue。
比如你的代码片段:
A operator=(const A& that){ return *this; // *this是lvalue,触发拷贝构造生成prvalue返回值 }
这里a2 = a3作为函数调用表达式,其值类别就是prvalue,而非xvalue。
2. 临时物化是语境相关的转换,不改变表达式原始类别
临时物化是prvalue转换为xvalue的过程,但这个转换仅在需要将prvalue当作对象使用的语境下才会发生:
- 绑定到右值引用:
A&& temp = a2 = a3;,此时prvalue会被物化为临时对象,表达式a2=a3在此语境下转换为xvalue。 - 访问成员:
(a2=a3).m,同样会触发物化,表达式变为xvalue。
但在你的decltype((expr))测试中,expr是a1=a2=a3,这个表达式的原始值类别就是prvalue,decltype会直接返回其原始类别对应的类型A,因此你的value_category模板匹配的是基础模板,输出"prvalue"。
3. 输出中临时对象的来源
你看到的拷贝构造调用,是因为prvalue被物化生成临时对象:
a2 = a3:函数调用返回prvalue,当该prvalue作为a1 = ...的实参时,需要物化生成临时对象,触发拷贝构造。a1 = temp:同理,operator=返回的prvalue再次被物化,触发第二次拷贝构造。
这些临时对象是prvalue物化后的产物,但生成它们的表达式(函数调用)本身仍是prvalue,只有在物化发生的特定语境下,表达式才会被当作xvalue处理。
验证:触发xvalue的场景示例
如果修改测试代码,让表达式进入需要物化的语境,就能看到xvalue的结果:
// 将prvalue显式转换为xvalue PRINT_VALUE_CAT(static_cast<A&&>(a1=a2=a3)); // 输出:static_cast<A&&>(a1=a2=a3) is a xvalue // 绑定到右值引用后,用std::move转换为xvalue A&& ref = a1=a2=a3; PRINT_VALUE_CAT(std::move(ref)); // 输出:std::move(ref) is a xvalue
总结
- 返回非引用类型的函数调用表达式,其原始值类别就是prvalue,这是C++标准的明确规定,和是否生成临时对象无关。
- 临时对象是prvalue物化的结果,物化过程会将prvalue转换为xvalue,但这是语境相关的转换,不会改变表达式本身的原始类别。
- 你的
decltype((expr))测试的是表达式的原始值类别,而非物化后的类别,因此输出prvalue。
内容的提问来源于stack exchange,提问作者Yue Zhou
相关产品推荐
相关产品推荐

