C++固定长度向量:如何对std::initializer_list做编译期长度检查?
编译期检查vec初始化列表长度的解决方案
由于std::initializer_list<T>的size()是运行时属性,标准不保证编译器能在编译期获取其长度,因此直接用static_assert无法实现需求。我们可以通过以下几种方案替代,实现编译期的长度与类型检查:
方案1:模板参数包构造函数(推荐)
使用可变模板参数包接收初始化元素,通过C++20的requires约束在编译期检查元素数量与类型:
template <nsp_length::length_t L, nsp_concepts::is_numeric T> class vec { public: using array_type = std::array<T, L>; // 替换原initializer_list构造函数 template <typename... Args> requires (std::same_as<std::decay_t<Args>, T> && ...) && (sizeof...(Args) == L) vec(Args&&... args) : data_v{std::make_unique<array_type>(std::array<T, L>{std::forward<Args>(args)...})} {} private: std::unique_ptr<array_type> data_v; };
优势:
- 保留了
vec<3, int>{1, 2, 3}的直观初始化语法 - 编译期直接报错:元素数量不符或类型不匹配时,编译器会在编译阶段抛出错误,无需运行时检查
- 支持完美转发,避免不必要的拷贝
方案2:基于std::array的构造函数
让用户通过std::array初始化vec,利用std::array的编译期固定大小特性做检查:
template <nsp_length::length_t L, nsp_concepts::is_numeric T> class vec { public: using array_type = std::array<T, L>; // 精确匹配大小的构造函数 vec(const array_type& arr) : data_v{std::make_unique<array_type>(arr)} {} // 带编译期检查的通用数组构造函数 template <std::size_t N> vec(const std::array<T, N>& arr) : data_v{std::make_unique<array_type>()} { static_assert(N == L, "初始化数组的大小必须与vec长度匹配"); std::copy(arr.begin(), arr.end(), data_v->begin()); } private: std::unique_ptr<array_type> data_v; };
使用方式:
// 编译期通过 vec<3, int> v1(std::array{1, 2, 3}); // 编译期报错,数组大小为2,vec长度为3 vec<3, int> v2(std::array{1, 2});
方案3:结合类模板参数推导(CTAD)自动推导长度
如果希望用户无需显式指定L,可以通过推导指引让编译器从初始化参数推导出vec的长度:
template <nsp_length::length_t L, nsp_concepts::is_numeric T> class vec { public: using array_type = std::array<T, L>; template <typename... Args> requires (std::same_as<std::decay_t<Args>, T> && ...) vec(Args&&... args) : data_v{std::make_unique<array_type>(std::array<T, L>{std::forward<Args>(args)...})} {} private: std::unique_ptr<array_type> data_v; }; // 推导指引:从参数数量推导L,从第一个参数推导T template <typename T, typename... Args> vec(T, Args...) -> vec<1 + sizeof...(Args), T>;
使用方式:
// 自动推导为vec<3, int> vec v{1, 2, 3}; // 编译期报错:参数数量不匹配(若显式指定L) vec<2, int> v{1, 2, 3};
为什么原initializer_list方案无法实现编译期检查?
std::initializer_list的size()成员函数是运行时计算的,即使初始化列表是字面量(如{1,2,3}),C++标准也不要求编译器在编译期暴露这个值。因此无法通过static_assert直接检查其长度,只能通过上述替代方案实现编译期约束。
内容的提问来源于stack exchange,提问作者Francisco Zavala
相关产品推荐
相关产品推荐

