为何class{}.refmem是左值表达式?右值对象引用成员访问规则解析
关于右值对象访问引用类型成员得到左值的原理
首先明确C++值类别(value category)的核心判定逻辑:表达式的值类别由其本身的属性决定,而非所属对象的生命周期。
1. 成员访问表达式的标准规则
C++标准对成员访问的左值/右值判定有明确规定:
- 当访问的非静态成员不是引用类型时(比如示例中的
S{}.val),表达式的值类别和对象表达式的类别一致:S{}是临时右值对象,因此S{}.val属于xvalue(亡值,右值的子类)。 - 当访问的非静态成员是引用类型时(比如
S{}.rval),该表达式的行为等价于解引用指向引用绑定对象的指针,而解引用指针的结果是左值——因为引用本质是对象的别名,访问引用成员就是获取它绑定的对象,而这个对象(此处为val)本身是左值。
2. 底层逻辑:引用本质与生命周期的区分
你混淆了引用的生命周期和引用绑定对象的生命周期:
- 临时对象
S{}会在当前语句结束时销毁,成员val也会随之消亡,但S{}.rval作为表达式,其语义是返回对val的引用——在表达式求值的瞬间,val仍然存活,而引用类型成员访问的规则就是暴露它绑定的左值对象,不管所属整体是不是临时对象。 - 换个角度:引用不是独立对象,只是别名。访问
rval时,编译器直接将其替换为val的别名,val虽属于临时对象,但本身是可寻址的左值(你可以合法取&S{}.rval,结果就是&val),因此整个表达式自然是左值。
3. 代码示例验证
以下扩展代码能直观体现规则:
struct S { int val = 42; int& rval = val; }; int main() { // S{}.rval是左值,可直接赋值 S{}.rval = 100; // S{}.val是xvalue(C++17后允许右值赋值) S{}.val = 200; // 取地址验证:两者都可寻址,但绑定的对象会随临时结构体销毁 int* p1 = &S{}.rval; int* p2 = &S{}.val; }
注意:虽然S{}.rval是左值,但绑定的val会在语句结束后销毁,后续通过p1访问内存会触发未定义行为。
内容的提问来源于stack exchange,提问作者Oersted
相关产品推荐
相关产品推荐

