C++14下如何用初始化列表初始化constexpr Integer类?
解决C++14下constexpr构造大整数模板类的编译错误
我编写了一个表示大整数的模板类Integer,尝试添加constexpr构造函数来创建编译时常量,但在GCC 9.4.0(C++14标准)下编译失败,错误提示构造函数无法作为constexpr函数调用。原始代码及错误信息如下:
template<typename ElementType, uint32_t ElementNum> class Integer final{ public: constexpr Integer(const std::initializer_list<ElementType> &l){ auto iter = l.begin(); for (uint32_t i = 0; i < ElementNum; i++) { if (iter != l.end()) { value_[i] = *iter; iter++; } else { value_[i] = 0; } } }; constexpr Integer(const std::array<ElementType, ElementNum> &v){ for (uint32_t i = 0; i < ElementNum; i++) { value_[i] = v[i]; } }; constexpr explicit Integer(const ElementType data[ElementNum]){ for (uint32_t i = 0; i < ElementNum; i++) { value_[i] = data[i]; } }; private: ElementType value_[ElementNum]; }; int main() { //error: expression must have a constant value constexpr Integer<uint32_t, 2> a({1, 2}); constexpr std::array<uint32_t, 2> t({1,2}); //error: expression must have a constant value constexpr Integer<uint32_t, 2> b(t); constexpr uint32_t p[2] = {1,2}; //error: expression must have a constant value constexpr Integer<uint32_t, 2> c(p); return 0; }
错误原因
- C++14标准中,
std::initializer_list的begin()、end()成员函数并非constexpr,无法在constexpr构造函数中使用迭代器遍历。 std::array的operator[]在C++14中不是constexpr,直接通过v[i]访问元素无法在编译期完成。- C数组参数会退化为指针,
constexpr上下文无法确认指针指向的是编译期常量数组,导致构造函数调用失败。
修改后的代码
#include <array> #include <cstdint> #include <type_traits> template<typename ElementType, uint32_t ElementNum> class Integer final{ public: // 1. 参数包构造:替代initializer_list,支持编译期初始化并自动补全0 template<typename... Args, typename = typename std::enable_if<sizeof...(Args) <= ElementNum>::type> constexpr Integer(Args&&... args) : value_{static_cast<ElementType>(args)...} { // 补全参数包长度不足的元素为0 using dummy = int[]; (void)dummy{0, (value_[sizeof...(Args) + _] = 0, 0)...}; } // 2. std::array构造:通过递归模板实现编译期遍历赋值 constexpr Integer(const std::array<ElementType, ElementNum>& v) noexcept { assign_from_array(v); } // 3. C数组引用构造:避免指针退化,支持编译期访问 constexpr explicit Integer(const ElementType (&data)[ElementNum]) noexcept { assign_from_carray(data); } private: // 递归模板:从std::array赋值 template<uint32_t Index = 0> constexpr void assign_from_array(const std::array<ElementType, ElementNum>& v) noexcept { if (Index < ElementNum) { value_[Index] = *(v.data() + Index); assign_from_array<Index + 1>(v); } } // 递归模板:从C数组赋值 template<uint32_t Index = 0> constexpr void assign_from_carray(const ElementType (&data)[ElementNum]) noexcept { if (Index < ElementNum) { value_[Index] = data[Index]; assign_from_carray<Index + 1>(data); } } ElementType value_[ElementNum]; }; int main() { // 用参数包初始化(替代initializer_list方式) constexpr Integer<uint32_t, 2> a(1, 2); // 用std::array初始化 constexpr std::array<uint32_t, 2> t{1, 2}; constexpr Integer<uint32_t, 2> b(t); // 用C数组初始化 constexpr uint32_t p[2] = {1, 2}; constexpr Integer<uint32_t, 2> c(p); // 编译期验证初始化结果 static_assert(a.value_[0] == 1 && a.value_[1] == 2, "a initialization failed"); static_assert(b.value_[0] == 1 && b.value_[1] == 2, "b initialization failed"); static_assert(c.value_[0] == 1 && c.value_[1] == 2, "c initialization failed"); return 0; }
修改说明
参数包构造函数
- 直接用参数包初始化成员数组,C++14允许在
constexpr构造函数中初始化聚合类型(数组)。 - 通过
std::enable_if限制参数数量不超过ElementNum,避免传入过多参数导致编译错误。 - 用扩展初始化列表补全剩余元素为0,确保数组所有元素都被初始化。
- 直接用参数包初始化成员数组,C++14允许在
std::array构造函数
- 改用递归模板函数实现编译期遍历,C++14支持
constexpr递归函数。 - 通过
v.data()获取数组首地址,C++14中std::array::data()的const版本是constexpr,可在编译期访问元素。
- 改用递归模板函数实现编译期遍历,C++14支持
C数组引用构造函数
- 将参数改为数组引用
const ElementType (&data)[ElementNum],避免数组退化为指针,编译器能在编译期确认数组的常量属性。 - 同样用递归模板函数完成编译期赋值。
- 将参数改为数组引用
内容的提问来源于stack exchange,提问作者lei hu
相关产品推荐
相关产品推荐

