c++隐式移动构造、强制拷贝省略、返回具名局部变量

翻译:

隐式移动(implicit move on return)

强制要求拷贝省略(mandatory copy elision)



一、问题:

"代码段:

c 复制代码
std::string make() { return std::string("hello"); }

std::string a = make();          // ①
std::string b = a;               // ②
std::string c = std::move(a);    // ③
  • ① make() 是 prvalue(右值)。编译器在重载决议时看到实参是右值,自动挑中 string(string&&) 移动构造。你没写 std::move,但移动发生了 → 这就是"隐式移动"。
    ",
    这一段说得对吗?
    "自动挑中 string(string&&) 移动构造"说的是"std::string("hello")"吗?



简短回答:这段话大方向没错,但不够严谨,而且你问的那个细节理解反了------先说这个。

"自动挑中 string(string&&)" 说的是谁?

不是 std::string("hello") 这一步。

std::string("hello") 这行代码本身,调用的永远是 string(const char*) 构造函数------它负责把字符串内容 "hello" 构造出来(分配内存、拷贝字符等)。这一步跟移动构造完全无关,不管上下文如何都不会变。

"移动构造 string(string&&)" 指的是另外一步 :当这个刚构造好的临时对象,要被转移 给别的对象(比如函数的返回值位置,或者变量 a)时,用哪个构造函数去做这个"转移"动作。移动构造函数做的事情是"偷"走临时对象内部的堆指针,而不是重新分配内存拷贝内容。

所以完整来看应该是两步:

  1. string(const char*) 构造出临时对象(内容是 "hello")
  2. 把这个临时对象的资源转移给目标位置------这一步理论上会调用 string(string&&)

但这里有个更关键的问题:①在 C++17 下根本不会调用移动构造

这段材料说"自动挑中移动构造",这个说法对应的是 C++11/14 时代 或者关掉编译器优化(-fno-elide-constructors)时的行为。

但从 C++17 起 ,对于返回 prvalue(纯右值)的情况,标准强制要求拷贝省略(mandatory copy elision),这不是优化开关能关掉的,是语言规则本身。具体到你的例子:

cpp 复制代码
std::string make() { return std::string("hello"); }
std::string a = make();

在 C++17 下:

  • return std::string("hello");:临时对象直接在返回值的位置构造,没有"先造出来再搬运"这一说
  • std::string a = make();make() 返回的 prvalue 直接在 a 的内存位置上构造

从头到尾,只发生一次 string(const char*) 构造调用,移动构造函数根本没被调用。甚至语言标准都不要求移动构造函数是可访问的(因为反正不会用到它)。

所以严格讲,"编译器自动挑中了移动构造并调用它"这句话,在 C++17 语境下是不准确的------准确说法应该是"这里发生的是强制拷贝省略,连移动构造都省了"。

真正能演示"隐式移动"规则的例子

"隐式移动"这个概念,真正适用的场景是返回一个具名局部变量,而不是直接返回一个临时量:

cpp 复制代码
std::string make() {
    std::string local = "hello";
    return local;   // local 是具名左值,但作为返回值时按右值处理
}

这里如果编译器没做 NRVO(比如某些编译配置下),标准规定重载决议阶段会把 local 当成右值来看待,从而优先选中移动构造函数(找不到才退化为拷贝构造)------这才是标准里说的"隐式移动"(implicit move on return)的确切含义,且这个规则依赖于确实存在(且可访问)的移动构造函数。

你例子里真正明确调用移动构造的是③

cpp 复制代码
std::string c = std::move(a);    // ③

std::move(a) 把左值 a 转成右值引用类型,重载决议无歧义地选中 string(string&&)------这个才是百分百会发生、且能观察到的移动构造调用。

小结

步骤 实际发生什么
a = make() C++17 下是强制拷贝省略,没有移动构造调用
b = a 拷贝构造 string(const string&)
c = std::move(a) 明确调用移动构造 string(string&&)

如果这段材料是较早期(C++11 语境)的解释,把①说成"隐式移动"是当时教科书里常见的简化说法,但放到今天的编译器标准下就不严谨了,容易让人以为"移动构造函数真的被跑了一次",实际上它压根没被调用。

相关推荐
C++ 老炮儿的技术栈4 小时前
我们在设计tcp协议时,要传一个字符串过去,报文:头十长度十内容,是否要把‘\0‘也填入,长度是否包含‘\0‘
开发语言·数据结构·c++·mfc·c
传奇开心果编程5 小时前
【Rust入门练中学】第2课:变量、可变性与常量
开发语言·学习·rust
zr想努力5 小时前
从0开始配置Perforce4环境
开发语言
幸运小圣6 小时前
PointerEvent 常用属性与事件整理【JavaScript】
开发语言·javascript·ecmascript
长谷深风1117 小时前
评测AI Agent:三种裁判各司其职
java·大数据·开发语言·人工智能·ai agent
边境悍匪7 小时前
蜗牛学苑 Java 智能体学习 Day42|项目周开发技术汇总 1 思维导图复盘
java·开发语言·spring boot·学习·spring
君顾17 小时前
本地托管机构管理源码实战:需求拆解、数据建模与多端部署
java·开发语言·托管
传奇开心果编程9 小时前
【Rust入门练中学】 第3课:数据类型
开发语言·学习·rust
泡泡鱼(敲代码中)9 小时前
MySQL 学习笔记:DCL、函数与约束 —— 安全、效率、完整性的三板斧
开发语言·笔记·sql·学习·mysql·gitee
I_belong_to_jesus10 小时前
std::unique_ptr成员函数用法
c++