如何通过拷贝构造函数复制作为类成员的内置数组
解决方案
方案1:编译期索引序列+委托构造(通用无运行时开销,首推)
这是最稳妥的实现方式,所有逻辑在编译期完成,和手动逐个写初始化列表的性能完全一致,支持任意非默认可构造类型,不会触发未定义行为。
C++14 及以上版本实现
直接用标准库提供的std::index_sequence即可:
#include <utility> struct Bar{ Bar(int x) : x_(x){} int x_ = 0; }; constexpr size_t ARR_SIZE = 1000000; // 数组大小可自行修改 struct Foo{ Foo(); Foo(Foo const& rhs); Bar arr_[ARR_SIZE]; private: // 私有委托构造函数,接收编译期索引序列 template <std::size_t... Idx> Foo(Foo const& rhs, std::index_sequence<Idx...>) : arr_{rhs.arr_[Idx]...} {} // 给默认构造用的委托构造 template <std::size_t... Idx> Foo(std::index_sequence<Idx...>) : arr_{Idx...} {} }; // 默认构造不需要手写0到999999的初始化项 Foo::Foo() : Foo(std::make_index_sequence<ARR_SIZE>{}) {} // 拷贝构造自动生成所有元素的初始化项 Foo::Foo(Foo const& rhs) : Foo(rhs, std::make_index_sequence<ARR_SIZE>{}) {}
C++11 兼容实现
C++11 标准库没有提供std::index_sequence,自行实现一个极简版本即可使用,逻辑和上面完全一致:
#include <cstddef> // 自实现index_sequence兼容C++11 template <std::size_t... Is> struct index_sequence {}; template <std::size_t N, std::size_t... Is> struct make_index_sequence_impl : make_index_sequence_impl<N-1, N-1, Is...> {}; template <std::size_t... Is> struct make_index_sequence_impl<0, Is...> { using type = index_sequence<Is...>; }; template <std::size_t N> using make_index_sequence = typename make_index_sequence_impl<N>::type; // 后续Bar和Foo的实现和C++14版本完全一致 struct Bar{ Bar(int x) : x_(x){} int x_ = 0; }; constexpr size_t ARR_SIZE = 1000000; struct Foo{ Foo(); Foo(Foo const& rhs); Bar arr_[ARR_SIZE]; private: template <std::size_t... Idx> Foo(Foo const& rhs, index_sequence<Idx...>) : arr_{rhs.arr_[Idx]...} {} template <std::size_t... Idx> Foo(index_sequence<Idx...>) : arr_{Idx...} {} }; Foo::Foo() : Foo(make_index_sequence<ARR_SIZE>{}) {} Foo::Foo(Foo const& rhs) : Foo(rhs, make_index_sequence<ARR_SIZE>{}) {}
注意事项
如果数组大小超过编译器默认的模板递归深度(GCC默认是900,Clang默认是1024),编译时需要额外加参数调整递归深度,比如GCC下加:
-ftemplate-depth=1000010(数值比数组大小略大即可)
方案2:手动 placement new 构造(仅适用于特殊场景)
如果你的编译器对大长度索引序列的编译速度太慢,且你的类型是平凡可拷贝的(可以用std::is_trivially_copyable_v<Bar>验证),可以用下面的方案:
#include <cstring> #include <type_traits> struct Bar{ Bar(int x) : x_(x){} int x_ = 0; }; static_assert(std::is_trivially_copyable_v<Bar>, "Bar必须是平凡可拷贝类型才能用该方案"); constexpr size_t ARR_SIZE = 1000000; struct Foo{ Foo(); Foo(Foo const& rhs); ~Foo(); // 非平凡析构需要手动实现 // 用char数组占坑,手动管理对象生命周期 alignas(Bar) char arr_storage_[sizeof(Bar) * ARR_SIZE]; // 提供arr_的访问接口,兼容原有代码 Bar* arr_() { return reinterpret_cast<Bar*>(arr_storage_); } Bar const* arr_() const { return reinterpret_cast<Bar const*>(arr_storage_); } }; Foo::Foo() { for (size_t i = 0; i < ARR_SIZE; ++i) { new (arr_() + i) Bar(i); } } Foo::Foo(Foo const& rhs) { std::memcpy(arr_storage_, rhs.arr_storage_, sizeof(arr_storage_)); } Foo::~Foo() { for (size_t i = 0; i < ARR_SIZE; ++i) { arr_()[i].~Bar(); } }
警告:该方案仅适用于平凡可拷贝类型,非平凡类型用memcpy拷贝会直接触发未定义行为,且需要手动管理对象的构造析构,风险较高,非必要不推荐使用。
内容的提问来源于stack exchange,提问作者Itachi Uchiwa
相关产品推荐
相关产品推荐

