如何通过std::initializer_list初始化Point类的std::array成员?
问题描述
我在头文件中定义了如下模板类Point:
template<typename real> class Point { public: // 构造函数 Point(); Point(const std::initializer_list<real>&); Point(const std::initializer_list<real>&, const types::unitTypes); Point(const real, const real, const real); Point(const real, const real, const real, const types::unitTypes); ... private: std::array<real, 3> xyz_; types::unitTypes units_; };
该类需要支持通过std::initializer_list初始化,私有成员xyz_是std::array<real,3>类型。我尝试两种构造函数实现都失败:
- 初始化列表初始化
xyz_:
template<typename T> Point<T>::Point(const std::initializer_list<T>& xyz) : xyz_(xyz) , units_(types::au) {};
无法通过std::initializer_list直接构造std::array。
- 赋值方式:
template<typename T> Point<T>::Point(std::initializer_list<T> xyz) : units_(types::au) { xyz_ = xyz; };
std::array没有重载接受std::initializer_list的赋值运算符,编译失败。
我需要实现Point<real>({x, y, z})的调用,完成内部xyz_的初始化。
更新问题
我尝试定义构造函数Point(const std::array<real, 3>&),但出现编译歧义:
error: call of overloaded ‘Point(<brace-enclosed initializer list>)’ is ambiguous ... note: candidate: ‘aided::point::Point<real>::Point(const std::array<real, 3>&) [with real = float]’ ... note: candidate: ‘constexpr aided::point::Point<float>::Point(const aided::point::Point<float>&)’ ... note: candidate: ‘constexpr aided::point::Point<float>::Point(aided::point::Point<float>&&)’
第一个候选是我想调用的构造函数,后两个拷贝构造函数为何能被初始化列表触发?
解决方案
1. 正确实现std::initializer_list构造函数
std::array不支持直接用std::initializer_list构造或赋值,但可以通过遍历初始化列表的元素来逐个赋值:
template<typename T> Point<T>::Point(const std::initializer_list<T>& xyz) : units_(types::au) { if (xyz.size() != 3) { // 可选:处理元素数量不符的错误,比如抛出异常 throw std::invalid_argument("Initializer list must contain exactly 3 elements"); } std::copy(xyz.begin(), xyz.end(), xyz_.begin()); }
也可以用范围for循环实现:
template<typename T> Point<T>::Point(const std::initializer_list<T>& xyz) : units_(types::au) { if (xyz.size() != 3) { throw std::invalid_argument("Initializer list size must be 3"); } size_t idx = 0; for (const auto& val : xyz) { xyz_[idx++] = val; } }
这样就能通过Point<real>({x,y,z})完成初始化,同时还能校验输入元素的数量。
2. 解决std::array构造函数的歧义问题
出现歧义的原因是:编译器可以将{x,y,z}隐式转换为Point<real>(先通过std::array构造函数构造临时Point对象),再调用拷贝/移动构造函数;同时也可以直接用{x,y,z}构造std::array来调用你定义的构造函数,这两条路径的优先级相同,导致歧义。
解决方法有两种:
- 显式指定构造参数类型:调用时明确构造
std::array,比如Point<float>(std::array<float,3>{1.f,2.f,3.f})。 - 将
std::array构造函数改为explicit:
template<typename real> class Point { public: // ... 其他构造函数 ... explicit Point(const std::array<real, 3>&); // ... };
explicit关键字会禁止隐式转换,编译器就不会把{x,y,z}转换为临时Point对象,从而消除歧义。
另外,如果你的核心需求是支持{x,y,z}的列表初始化,其实直接用前面的std::initializer_list构造函数就足够,不需要额外定义std::array版本的构造函数。
内容的提问来源于stack exchange,提问作者drjrm3

