C++如何通过std::initializer_list实现静态数组的编译期构造
核心问题原因
你当前的代码无法实现编译期初始化主要有两个原因:
- 所有涉及初始化、维度校验、数据拷贝的函数都没有加
constexpr修饰,编译器无法将这些逻辑提升到编译期执行 StaticArray构造函数本身不是constexpr,无法构造编译期常量实例,自然也不支持在static_assert中访问成员
修改步骤
1. 给所有相关函数添加constexpr修饰
C++20起std::initializer_list的begin/end接口、std::all_of、std::ssize都已经是constexpr实现,同时constexpr函数允许抛出异常(编译期触发异常会直接报错,刚好符合维度校验的需求),你只需要给所有需要编译期执行的函数加上constexpr标记即可:
// checkDims加constexpr template <int k, typename Initializer, int... Sizes> constexpr void checkDims(const Initializer& init) { if constexpr (k < sizeof...(Sizes) - 1) { if (!checkNonJagged(init)) { throw std::invalid_argument("Jagged matrix initializer"); } } if (std::get<k>(std::forward_as_tuple(Sizes...)) != std::ssize(init)) { throw std::invalid_argument("Matrix initializer does not match with static matrix"); } if constexpr (k < sizeof...(Sizes) - 1) { checkDims<k + 1, decltype(*std::begin(init)), Sizes...>(*std::begin(init)); } } // addList两个重载加constexpr template <std::semiregular T> constexpr void addList(T* data, const T* first, const T* last, int& index) { for (; first != last; ++first) { data[index] = *first; ++index; } } template <std::semiregular T, typename I> constexpr void addList(T* data, const std::initializer_list<I>* first, const std::initializer_list<I>* last, int& index) { for (; first != last; ++first) { addList(data, first->begin(), first->end(), index); } } // insertFlat加constexpr template <std::semiregular T, typename I> constexpr void insertFlat(T* data, std::initializer_list<I> list) { int index = 0; addList(data, std::begin(list), std::end(list), index); } // StaticArray构造函数加constexpr template <std::semiregular Scalar, int... Sizes> constexpr StaticArray<Scalar, Sizes...>::StaticArray(DenseInitializer_t<value_type, N> init) { verifyDims(init); insertFlat(data(), init); }
2. 调整底层存储支持编译期访问
StaticArray的底层存储需要使用支持constexpr访问的类型,推荐直接用std::array<Scalar, (Sizes * ...)>作为底层容器,它的data()、operator[]接口在C++20下都是constexpr实现,完全满足需求。
3. 声明编译期常量实例
如果要在static_assert中访问数组元素,需要把实例声明为constexpr:
constexpr frozenca::StaticArray<float, 2, 3> arr2 {{1, 2, 3}, {4, 5, 6}}; static_assert(arr2[{1, 1}] == 5); // 现在可以正常编译通过
版本要求
以上修改需要C++20及以上标准支持,编译时需要指定对应的标准参数(比如GCC/Clang加-std=c++20,MSVC加/std:c++20)。
内容的提问来源于stack exchange,提问作者frozenca
相关产品推荐
相关产品推荐

