C++17中能否在成员初始化列表完成不可移动对象的两步初始化?
问题背景
我正在使用一套遗留API,其中类A没有默认、拷贝、移动构造函数及赋值运算符,必须通过构造函数+init方法完成两步构造;类B必须依赖完全初始化的A进行构造。我尝试编写类C,其成员包含A和B,期望在C的构造函数成员初始化列表中完成A的两步初始化后再初始化B,但多种尝试均失败,目前用unique_ptr实现,想寻找C17下的更优方案(无法使用C20的保证移动消除规则)。
遗留API代码
class A { public: A(int some_param); // 无默认、拷贝、移动构造函数及赋值运算符 A() = delete; A(const A&) = delete; A(A&&) = delete; // 本不该删除,但无法修改该类 A& operator= (const A&) = delete; A& operator= (A&&) = delete; // 两步构造:构造后需调用init完成初始化 void init(int some_other_argument); // 其他成员 }; class B { public: // 无默认构造函数,必须传入完全初始化的A B() = delete; B(const A&); // 其他成员 };
目标代码(期望实现的结构)
class C { public: C(); private: A m_a; B m_b; }; C::C() : m_a{ /* 完成A的两步初始化 */} , m_b(m_a) {}
尝试过的无效方案
- 立即初始化lambda:编译失败,因为
A不可移动
C::C() : m_a{[] { auto a = A(0xdead_beef); a.init(42); return a; // 编译错误:A不可移动 }()} , m_b(m_a) {}
- 构造函数体赋值:编译失败,因为
A和B的默认构造函数已删除,进入函数体前会尝试默认初始化成员
C::C() // m_a和m_b会被尝试默认初始化,但它们的默认构造函数已删除 { m_a = A(0xdead_beef); m_a.init(); m_b = B(m_a); }
- 构造函数体中完成第二步初始化:逻辑错误,初始化
m_b时m_a还未完成init,不符合B的构造要求
C::C() : m_a(0xdead_beef) , m_b(m_a) // 此时m_a未完成init,不符合B的构造条件 { m_a.init(); }
当前实现方案
使用std::unique_ptr间接持有A,确保A完成初始化后再构造B:
class C { public: C(); private: std::unique_ptr<A> m_a; // 构造函数中保证初始化,无需空指针检查 B m_b; }; C::C() : m_a{[] { auto a = new A(0xdead_beef); a->init(42); return a; }()} , m_b(*m_a) {}
C++17下的更优方案
可以用嵌套类封装A的两步初始化,利用类成员的初始化顺序(嵌套类对象先于外部类的B成员初始化),避免动态内存分配:
class C { private: // 嵌套类:负责完成A的两步初始化 class AHolder { public: A a; AHolder() : a(0xdead_beef) { a.init(42); } }; AHolder m_a_holder; B m_b; public: C() : m_b(m_a_holder.a) {} // 对外暴露A的接口(如果需要) A& get_a() { return m_a_holder.a; } const A& get_a() const { return m_a_holder.a; } };
方案优势
- 避免了
unique_ptr带来的动态内存分配和间接访问开销 - 严格保证
A完成初始化后才构造B:m_a_holder作为C的第一个成员,会先被初始化,其构造函数中完成A的init,之后再初始化m_b - 完全符合C++17标准,无需依赖后续版本特性
内容的提问来源于stack exchange,提问作者Robin
相关产品推荐
相关产品推荐

