构造函数表达式的值类别是:prvalue

为什么 C++ 中构造表达式的结果是 prvalue?

这是 C++ 值类别(value category)设计的核心问题之一。先说明一点:构造函数本身并不"返回值",准确的说法是,构造表达式 T() 或 T(args...) 的值类别是 prvalue。

一、值类别回顾

C++11 之后,表达式主要分为三类:

值类别 含义
lvalue(左值) 有身份、可取地址的对象,不能被隐式移走
xvalue(将亡值) 有身份,但其资源可以被移走,例如 std::move(x)
prvalue(纯右值) 没有身份,用来初始化对象或计算运算符的操作数

prvalue 不是对象本身,而是对象的初始化器,它描述"如何构造一个对象"。

二、为什么这样设计

1. 支持强制复制消除(C++17 起)

cpp 复制代码
T x = T(1, 2);
// C++17 前:允许编译器消除临时对象,但不是强制的,
//          且拷贝/移动构造函数必须可访问
// C++17 起:保证直接在 x 的位置构造,零开销

prvalue 的语义是"描述如何初始化一个对象",而不是"已经存在的对象"。因此编译器可以直接在目标位置构造,不需要中间临时对象。

这也带来一个直接的好处:不可拷贝、不可移动的类型也能这样初始化。

cpp 复制代码
struct Pinned {
    Pinned() = default;
    Pinned(const Pinned&) = delete;
    Pinned(Pinned&&) = delete;
};

Pinned p = Pinned();  // C++17 起合法,C++14 及之前编译失败

2. 返回值优化的语言保证

cpp 复制代码
T make() { return T(); }
T x = make();  // C++17 保证:直接构造 x,无拷贝无移动

如果 T() 产生的是 lvalue(一个已存在的对象),就必须先构造这个对象,再把它移动或拷贝到目标位置。prvalue 让"延迟物化"成为可能。

注意 :C++17 保证的是返回 prvalue 时的省略。对于具名局部变量的返回(NRVO),编译器通常会优化,但标准不保证:

cpp 复制代码
T make2() {
    T t;
    return t;  // NRVO:允许但不保证,T 仍需可移动
}

3. 引用绑定与生命周期

cpp 复制代码
const T& r = T();  // prvalue 绑定到 const 引用,生命周期延长至 r 的作用域结束
T&& rr = T();      // 同理

prvalue 在绑定到引用时才会"物化"(temporary materialization)为临时对象,物化的结果是一个 xvalue,其生命周期被自动延长。

4. 语义更精确

cpp 复制代码
T()           // prvalue:描述"如何构造一个 T"
std::move(x)  // xvalue:描述"x 可以被移走"
x             // lvalue:描述"x 这个对象"

三者职责分明:

  • prvalue:初始化来源
  • xvalue:移动来源
  • lvalue:持久对象

总结

prvalue 不是对象,而是对象的"配方"。 这样设计让编译器可以把"创建临时对象 → 拷贝/移动到目标"优化成"直接在目标位置构造"。从 C++17 起,这不再是编译器的可选优化,而是语言层面的保证。

修正日志:
  1. 标题和表述 :构造函数本身没有"值",准确说法是构造表达式 T(args...) 的值类别是 prvalue。
  2. RVO 的范围 :C++17 保证的只是返回 prvalue 时的省略(如 return T();)。具名返回值优化(NRVO,如 T t; return t;)并不保证,原文容易让人误解。
  3. 临时对象物化:补充了 prvalue 物化后得到的是 xvalue。
  4. 补充了不可拷贝/不可移动类型的例子,这是强制消除最直观的价值。
相关推荐
starzy19904 小时前
深入理解Scala隐式值:编译期自动装配
开发语言·后端·scala
布莱克6055 小时前
Swift 条件语句详解:从 if 到 switch 的完整指南
开发语言·ssh·swift·switch·条件语句·guard·if
汉克老师5 小时前
《从C++小白到算法高手:GESP 1—8级成长地图》第一讲:认识GESP——从编程新手到算法探索者
c++·gesp·信息学竞赛·小学生学c++
吴可可1235 小时前
ActiveX调用WScript.Shell原理揭秘
开发语言
晴天的雨.9926 小时前
【C++】继承篇
java·开发语言·c++
SomeOtherTime6 小时前
第 1 讲:内存布局与所有权解剖——栈帧、胖指针与“移动/拷贝”在汇编层面的真实面目
java·开发语言·汇编
青少儿编程课堂6 小时前
蓝桥杯青少组图形化 + 全国青少年信息素养大赛 + 电子学会图形化考级:校园大富翁经营闯关解析
c++·python·算法·bfs·信息学竞赛
another heaven6 小时前
【C/C++ 第一次跨越(上):用 C++11/14 停止制造新的旧代码】
c语言·c++·制造
qq_406981036 小时前
c++ lamda(三)Lambda表达式
java·前端·c++
总要学点什么7 小时前
第106篇:三维特效-流动线效果——让一颗光点沿整条线一路滚到头
开发语言·javascript·ecmascript·gis·webgl·材质·cesium