如何让自定义构造函数实现类B那样的聚合初始化行为?
问题:自定义构造函数模拟聚合初始化的无拷贝效果
先看示例代码:
#include <iostream> using namespace std; struct A { A() { cerr << "default" << endl; } A(const A &a) { cerr << "copy" << endl; } A(A &&a) { cerr << "move" << endl; } }; struct B { A a; }; struct C { A a; C(A a): a(a) {} }; int main() { B b {A()}; cerr << "=======" << endl; C c {A()}; return 0; }
编译运行输出:
default ======= default copy
可见类B的聚合初始化直接就地构造成员a,无额外拷贝;而类C的自定义构造函数会先构造参数对象,再拷贝到成员。即使修改为C(A a): a(forward<A>(a)) {},也只能实现移动而非就地构造的效果。如何让自定义构造函数复刻聚合初始化的无拷贝/无移动行为?
解决方案
核心思路是让构造函数直接在成员对象的内存空间中构造实例,跳过中间参数对象的创建,这和聚合初始化的本质逻辑一致。具体可以通过模板化完美转发构造函数实现:
基础实现
struct C { A a; // 完美转发所有参数,直接在成员a的位置构造对象 template<typename... Args> C(Args&&... args) : a(std::forward<Args>(args)...) {} };
修改后运行代码,输出会和类B的情况完全一致:
default ======= default
原理说明
聚合初始化是直接在成员的内存地址上调用构造函数;而模板转发构造函数会把传入的参数直接传递给成员a的构造函数,同样实现就地构造,不会产生中间的临时对象,自然没有拷贝或移动操作。
之前的传值构造函数(包括带forward的版本)之所以无法做到,是因为参数a是一个独立的临时对象,必须先构造它,再通过移动/拷贝赋值给成员,无法避免额外操作。
可选:添加类型约束(避免滥用)
如果想限制构造函数仅接受能合法构造A的参数,可以用SFINAE添加约束,防止意外的类型转换:
#include <type_traits> struct C { A a; template<typename... Args, // 仅当参数可构造A时启用该构造函数 std::enable_if_t<std::is_constructible_v<A, Args...>, int> = 0> C(Args&&... args) : a(std::forward<Args>(args)...) {} };
内容的提问来源于stack exchange,提问作者Nanored
相关产品推荐
相关产品推荐

