不同C++标准下临时对象生存期定义的疑问
我太懂这种困惑了——刚对比C17前后关于临时对象创建的规则时,第一眼真的会觉得这俩列表完全不搭边,尤其是函数返回prvalue这个点,直觉上好像C17之后也该有临时对象,但规则里的分类好像完全没覆盖,对吧?咱们一点点拆解清楚:
首先得明确,C17的一个核心变化是引入了prvalue物化的概念——简单说就是,prvalue本身是“纯右值”,是一个值的描述,不是实际的对象;只有当它需要被当作glvalue(可以取地址的“泛左值”)使用时,才会被“物化”成一个临时对象。而C17之前的规则,其实是把所有需要创建临时对象的场景直接列出来,没有明确区分“prvalue”和“物化”的概念。
先把两边的规则整理成清晰的列表:
C++17 之前触发临时对象创建的场景
- 绑定引用到prvalue
- 用大括号初始化列表初始化
std::initializer_list类型对象 - 从函数返回prvalue
- 生成prvalue的转换操作(包括
T(a, b, c)和T{}这种形式) - lambda表达式(早期会生成临时闭包对象)
- 需要转换初始值的复制初始化、向不同但可转换类型或位域的引用初始化
C++17 及之后触发prvalue物化(即创建临时对象)的场景
- 对类prvalue进行成员访问时
- 对数组prvalue执行数组转指针转换或下标操作时
sizeof和typeid中的未求值操作数- prvalue作为废弃值表达式出现时
关键疑问的解答:函数返回prvalue在C++17之后去哪了?
这就是你困惑的核心点对吧?在C17之前,函数返回prvalue必须创建临时对象,然后再把这个临时对象的值拷贝/移动到目标对象里(当然编译器可能会优化掉,但标准允许创建)。但C17之后,标准直接规定函数返回的prvalue可以直接用来初始化调用端的目标对象,完全跳过了“创建临时对象再复制”的步骤——这就是所谓的“强制复制消除”(mandatory copy elision)。
也就是说,C17之后,函数返回prvalue的场景根本不需要物化临时对象了,所以它自然不会出现在C17的物化列表里。这也是为什么看起来前后规则不重叠的原因:C++17把很多之前需要创建临时的场景,通过复制消除直接跳过了临时对象的创建,只保留了那些必须把prvalue变成实际对象才能操作的场景。
另外还要提一句,C++标准一直要求“临时对象的物化要尽可能延迟”,目的就是避免创建不必要的临时对象,这就是复制消除规则的核心逻辑——能不创建临时就不创建。
内容来源于stack exchange

