构造函数表达式的值类别是: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. 补充了不可拷贝/不可移动类型的例子,这是强制消除最直观的价值。
相关推荐
小嘉丶学长1 小时前
Day06 · 数据类型与类型检测
开发语言·python·学习·1024程序员节
wuminyu1 小时前
JVM规范学习总结-Class文件结构
java·linux·c语言·jvm·c++
多弗朗皮卡丘1 小时前
C++特殊类设计
开发语言·c++·设计模式
Wang's Blog1 小时前
Java 项目实战: 交友项目 - 查询单条动态:动态详情页的接口与实现
java·开发语言·交友
yuniko-n1 小时前
【JUC】CAS
java·开发语言
紧跟先前的步伐1 小时前
Go AI Agent 实战 17|模型适配层设计:让模型可替换
开发语言·人工智能·后端·ai·golang
小灰灰搞电子2 小时前
Qt 中使用 QSettings 存储结构体数据详解
开发语言·qt
外收内放2 小时前
FastAPI猜字谜实战(5):会话管理
开发语言·python·学习·fastapi
Ai-_Man2 小时前
Microsoft Copilot对话数据怎么导入到另外一个Microsoft Copilot账号里去?AI导出鸭
开发语言·javascript·人工智能·microsoft·chatgpt·ecmascript·copilot