无拷贝/移动构造函数的类,其std::array作为类成员时如何初始化?
问题:如何初始化包含不可拷贝类的std::array成员的类?
假设有一个无默认构造函数的类A,其拷贝构造函数与拷贝赋值运算符已被显式删除。可以直接创建A的std::array对象(如下方代码中的l_Array),但当该std::array作为另一个类B的成员时,初始化B(如下方代码中的b)会出现编译错误。请问是否可以实现该类的初始化?
代码示例
#include <iostream> #include <array> class A { public: explicit A(int x) : m_x {x} {} A(const A &) = delete; virtual ~A() {} A & operator=(const A &) = delete; private: int m_x; }; template <std::size_t N> class B { public: template <typename ... Types, typename std::enable_if_t<sizeof...(Types) == N, int> = 0> constexpr explicit B(Types&& ... s) : m_t_Array {static_cast<A>(s)...} { } private: std::array<A, N> m_t_Array; }; int main(int, char**) { std::array<A, 2> l_Array { A{1}, A{2} }; B<2> b {A{1}, A{2}}; return 0; }
编译错误输出
<source>:21:54: error: static_cast from 'A' to 'A' uses deleted function constexpr explicit B(Types&& ... s) : m_t_Array {static_cast<A>(s)...} ^~~~~~~~~~~~~~~~~ <source>:30:10: note: in instantiation of function template specialization 'B<2>::B<A, A, 0>' requested here B<2> b {A{1}, A{2}}; ^ <source>:8:5: note: candidate constructor has been explicitly deleted A(const A &) = delete; ^ 1 error generated.
解决方案
问题核心是:直接初始化std::array<A,2>时用的是聚合初始化,会直接在std::array的内存空间里构造每个A对象,不需要拷贝/移动;但你写的B构造函数里,static_cast<A>(s)...会先创建临时A对象,再尝试拷贝到std::array中,触发了被删除的拷贝构造函数。
有两种可行的修复方式:
方式一:完美转发参数给std::array聚合初始化
修改B的构造函数,直接转发参数,让std::array直接构造内部的A对象,避免多余的拷贝:
template <std::size_t N> class B { public: template <typename ... Types, typename std::enable_if_t<sizeof...(Types) == N, int> = 0> constexpr explicit B(Types&& ... s) : m_t_Array { std::forward<Types>(s)... } { } private: std::array<A, N> m_t_Array; };
这里std::forward会保持参数的右值/左值属性,std::array的聚合初始化会直接用传入的参数在自身内存中构造A实例,完全绕过拷贝构造。
方式二:使用定位new直接构造
如果希望支持用原始值(比如int)初始化,可以给B添加接受初始化列表的构造函数,通过定位new直接在std::array的内存里构造A:
template <std::size_t N> class B { public: constexpr explicit B(std::initializer_list<int> init) { static_assert(init.size() == N, "初始化列表长度不匹配"); std::size_t idx = 0; for (int val : init) { // 直接在array的对应位置构造A对象 new (&m_t_Array[idx]) A{val}; ++idx; } } private: std::array<A, N> m_t_Array; };
使用时可以直接写B<2> b {1, 2};,同样不会触发拷贝构造。
两种方式都能正常通过编译,核心都是避免拷贝操作,直接在std::array的内存空间中构造A对象。
内容的提问来源于stack exchange,提问作者Guillaume BERLAND
相关产品推荐
相关产品推荐

