You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基类删除移动构造函数,派生类对象可返回的标准规则问询

问题解析与C++标准规则

核心结论

B的移动构造函数确实是被删除的,返回B对象的函数能编译通过,是因为触发了拷贝省略(Copy Elision),完全跳过了移动/拷贝构造函数的调用;而返回A对象的函数编译失败,是因为在未触发拷贝省略的场景下,编译器尝试调用被删除的A的移动构造函数。

1. A的f()编译失败的原因

当函数f()返回局部对象a时:

  • 在C++17之前,拷贝省略是编译器的可选优化。若编译器未执行优化,会尝试将局部对象a作为亡值(右值),优先调用A的移动构造函数完成返回操作。但A的移动构造函数被显式delete,该操作被禁止,因此编译报错。
  • 即使A的拷贝构造函数存在(=default),但因为移动构造被显式声明(哪怕是删除),编译器不会自动回退到拷贝构造(这是C++11引入移动语义后的规则:当右值可用时,优先尝试移动,移动不可用才会尝试拷贝,但显式删除移动构造会直接导致编译错误,而非回退)。

2. B的g()编译通过的原因

B的隐式移动构造函数状态

根据C++标准规则:

派生类的隐式移动构造函数,若基类的移动构造函数不可访问、被删除,或基类无法生成隐式移动构造函数,则派生类的隐式移动构造函数会被定义为删除的。

因此B的移动构造函数确实是被删除的,但g()能编译的关键在于拷贝省略:

  • 在函数返回同类型自动存储期对象的场景中(如g()返回局部对象b),编译器可以直接在函数返回值的内存位置构造b,完全跳过移动/拷贝构造函数的调用(这就是拷贝省略中的NRVO,即具名返回值优化)。
  • C17起,该场景的拷贝省略被强制要求,编译器必须执行,因此无论是否开启优化,g()都会编译通过;C17之前,只要编译器开启了默认优化(通常默认开启),就会执行拷贝省略,从而避免调用被删除的移动构造函数。

3. 相关C++标准规则摘要

拷贝省略规则

(C11 [class.copy.elision]/1;C17 [class.copy.elision]/1)
在以下场景中,编译器可以(C11)或必须(C17)省略拷贝/移动构造函数的调用:

  • 函数返回一个自动存储期的对象,且该对象的类型与函数返回类型相同,且该对象是函数体内的局部变量(或捕获的lambda变量)。

派生类隐式移动构造函数规则

(C11 [class.copy]/12;C17 [class.copy.ctor]/10)
当派生类的移动构造函数被隐式声明时:

  • 若基类的移动构造函数不可访问、被删除,或基类的移动构造函数是被隐式定义为删除的,则派生类的移动构造函数被定义为删除的。
  • 此外,若派生类的任何非静态数据成员无法被移动(如删除了移动构造),派生类的隐式移动构造函数也会被删除。

内容的提问来源于stack exchange,提问作者Yang Zhao

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.31 20:15:50