继承boost::noncopyable的类中默认移动构造函数为何无法编译?
问题描述
当TestClass继承自boost::noncopyable时,若使用TestClass(TestClass&& other) = default;声明默认移动构造函数,会编译失败,报错提示该构造函数因默认定义非法被隐式删除;但手动实现移动构造函数则可正常编译,这一现象的原因是什么?
对应的测试代码如下:
#include <iostream> #include <optional> #include <boost/noncopyable.hpp> class TestClass : public boost::noncopyable { public: TestClass() { std::cout << "Constructed...\n"; } TestClass(TestClass&& other) = default; //TestClass(TestClass&& other) { // std::cout << "Move constructor...\n"; //} TestClass& operator=(TestClass&& other) { std::cout << "Move assignment...\n"; return *this; } }; TestClass doThing() { std::optional<TestClass> tc = std::make_optional<TestClass>(TestClass{}); return std::move(*tc); } int main() { const TestClass t = doThing(); }
原因分析
boost::noncopyable的实现机制
boost::noncopyable通过将自身的拷贝构造函数和拷贝赋值运算符声明为private且不提供实现,强制子类无法进行拷贝操作。它本身并没有显式定义移动构造函数或移动赋值运算符。默认移动构造函数的生成规则
当使用= default声明移动构造函数时,编译器会尝试生成一个默认的移动构造函数,这个函数会递归地对所有基类和成员变量执行移动操作。
对于TestClass的基类boost::noncopyable,由于没有显式定义移动构造函数,编译器会尝试调用它的拷贝构造函数来完成"移动"(这是C++的 fallback 规则:当没有移动构造时,尝试用拷贝构造替代)。但boost::noncopyable的拷贝构造函数是private的,编译器无法访问,因此默认移动构造函数的生成会失败,最终被标记为隐式删除,导致编译报错。手动实现移动构造函数的差异
手动实现移动构造函数时,我们并没有显式处理基类boost::noncopyable的移动(因为它本身没有任何需要移动的成员变量或状态),编译器不会尝试去调用基类的拷贝或移动构造函数,因此可以绕过上述的访问权限问题,正常编译通过。
内容的提问来源于stack exchange,提问作者rmsantos

