为何C++编译器未在构造函数参数中自动使用移动构造?
问题:为何C++编译器未自动将构造函数参数移动到成员变量?
我一直认为C++编译器会在如下场景中使用移动构造:
#include <string> struct A { std::string str; explicit A(std::string s) : str(s) {} // 这里的s不会被移动到str里吗? }; std::string foo(int x) { return std::to_string(x); } int main() { A a(foo(10)); return a.str.size(); }
但通过Godbolt验证发现实际并非如此:在-O9优化级别下,将构造函数改为explicit A(std::string s) : str(std::move(s)) {}后生成的代码体积更小。我原以为两种写法等价,疑惑作为即将销毁的临时对象,s的值为何不会被自动移动到str中?
解答
核心原因是:构造函数的形参s是命名左值对象,而非临时亡值,C++标准不允许编译器自动将其当作右值处理,因此不会触发移动构造。
具体细节拆解:
- 调用
A a(foo(10))时,foo返回的临时字符串确实会被移动构造到形参s中——这一步是合法的,因为临时对象属于亡值,初始化形参时优先用移动构造。 - 但进入构造函数的初始化列表后,
s是一个有明确名称的局部变量,属于左值。哪怕它在构造函数结束后就会被销毁,C++标准也规定编译器不能自动将左值视为亡值——这是为了避免意外修改左值的行为,保证代码的可预测性。 - 因此
str(s)会调用std::string的拷贝构造函数,需要分配内存并复制字符串内容;而str(std::move(s))是显式将s转换为亡值,触发移动构造函数,仅需转移字符串的底层指针,无需内存分配和复制,生成的代码自然更精简。
简单来说:左值哪怕即将销毁,也不会被自动当作右值,必须显式用std::move转换,才能触发移动语义。
内容的提问来源于stack exchange,提问作者Lajos Nagy
相关产品推荐
相关产品推荐

