关于C++中对象成员/成员函数表达式值类别及标识性的技术问询
关于C++中对象成员/成员函数表达式值类别及标识性的技术问询
嘿,我来帮你把这些绕人的值类别问题掰明白,一步步拆解:
问题1:为什么S().m是xvalue,而S()是prvalue?它的标识性怎么体现?
首先回忆C++11重新定义的两个核心属性:有标识(identity)(能判断两个表达式是否指向同一个实体,比如通过内存地址)和可被移动。xvalue的特点是「有标识,但允许被移动」,而prvalue是「无标识,可被移动」。
S()是prvalue:它本质是一个"创建临时对象的蓝图",在C17之前(按你参考的资料语境,咱们就用C11-14的规则解释),这个表达式本身不对应一块已经分配的内存——没有可直接获取的地址,所以它没有标识,属于prvalue。S().m是xvalue:当你访问临时对象的非静态数据成员时,这个操作会强制临时对象实际分配内存(相当于在栈上创建了这个临时S对象),此时S().m明确指向了这个临时对象里的m成员——它有了具体的内存地址,也就是有了标识。
那怎么证明它的标识性?你可以把它绑定到右值引用,然后获取地址,就像你对std::move(a)做的那样:
#include <iostream> struct S { int m; }; int main() { int&& m_ref = S().m; std::cout << &m_ref << "\n"; // 输出临时S对象中m的内存地址 return 0; }
虽然这个临时对象的生命周期会被右值引用延长,但关键是:S().m指向的是一个具体的、有内存地址的实体,你能通过它的引用判断它和其他表达式是否指向同一个实体——比如另一个S().m的地址肯定和它不同,因为是另一个临时对象的成员。
至于S().m能和什么比较?你可以直接比较它的值(比如S().m == 0,如果m是默认初始化的话),也可以绑定到右值引用后,和其他指针/引用比较地址,以此判断是否是同一个实体。
问题2:为什么a.m(m是成员函数)是prvalue?和前面的逻辑有关吗?
这个和前面的S().m是xvalue的逻辑完全不冲突,核心区别在于你访问的是「数据成员」还是「成员函数」:
成员函数表达式a.m(不管a是左值还是右值),它代表的是函数本身的可调用句柄,而不是某个对象的实体:
- 对于非静态成员函数,
a.m本质上等价于成员函数指针&S::m(当你把它作为指针使用时),但作为表达式,它没有可被引用的内存地址——你不能写&(a.m),因为a.m不是一个对象,所以它没有对象级别的标识,属于prvalue。 - 对于静态成员函数,
a.m和全局函数的表达式一样,代表函数本身,同样没有对象级别的标识,也是prvalue。
而之前的S().m是xvalue,是因为它指向的是对象的一部分(有具体内存地址,有标识),和成员函数的表达式本质完全不同。
内容来源于stack exchange
相关产品推荐
相关产品推荐

