C++中A d(b+c)未触发移动构造函数?求原因与标准依据
问题解析:
A d(b+c)为何未调用移动构造函数? 示例代码
#include <iostream> using namespace std; class A{ char a; public: A(): a('#') { cerr << "DEFAULT!" << endl; }; A(char a): a(a) { cerr << "VALUE! " << a << endl; }; A(A const &o): a(o.a) { cerr << "COPY! " << o.a << endl; } // A(A && o): a(std::move(o.a)) { cerr << "MOVE!: " << o.a << endl; } // A(const A && o): a(std::move(o.a)) { cerr << "MOVE!(const): " << o.a << endl; } // A(volatile A && o): a(std::move(o.a)) { cerr << "MOVE!(volatile): " << o.a << endl; } // A(const volatile A && o): a(std::move(o.a)) { cerr << "MOVE!(const volatile): " << o.a << endl; } // A(A && o) = delete; // (***) friend A operator+(const A &o1, const A &o2){ A r; r.a = o1.a -'a' + o2.a; cerr << "+:" << r << endl; return r; } friend ostream& operator<<(ostream& out, const A& o) { out << o.a; return out; } }; int main(){ A a('a'); A b('b'); A c('c'); cout << a << b << c << endl; A d(b+c); // <----- !!! WHO HANDLES THIS??? !!! A e(a); cout << a << b << c << d << e << endl; return 0; }
问题描述
在A d(b+c)这一行,预期会调用默认移动构造函数,但手动重载的移动构造函数并未被触发;删除默认拷贝构造函数后,gcc/clang编译器报错提示默认移动构造函数已被删除。使用编译器版本为clang 10.0.0-4ubuntu1、g++ 9.4.0-1ubuntu1~20.04.2。
原因解释
1. 拷贝省略(Copy Elision)优化
编译器会执行返回值优化(RVO)/命名返回值优化(NRVO),直接在目标对象d的内存空间中构造operator+返回的临时对象,完全跳过拷贝或移动构造函数的调用流程。这就是为什么你看不到移动构造函数的输出——因为它根本没被执行。
可以通过添加编译选项-fno-elide-constructors关闭拷贝省略,此时如果存在合法的移动构造函数,就能看到它被调用的日志。
2. 默认移动构造函数的生成规则
根据C标准(C11及后续版本):如果类中存在用户显式声明的拷贝构造函数、拷贝赋值运算符或析构函数,编译器会自动抑制默认移动构造函数和移动赋值运算符的生成。
你的代码中显式定义了拷贝构造函数A(A const &o),因此类A没有默认生成的移动构造函数。当你删除拷贝构造函数后,A d(b+c)需要用移动构造来接收右值,但此时移动构造函数既没有用户定义,也没有默认生成,所以编译器报错。
标准参考
C标准规定:当类有用户声明的拷贝构造、拷贝赋值或析构函数时,隐式移动构造函数会被定义为删除的(或者说不被生成)。这是为了兼容C03的代码行为,避免意外的移动语义破坏原有逻辑。
内容的提问来源于stack exchange,提问作者Robert Szczelina
相关产品推荐
相关产品推荐

