You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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;
}

修改说明

  1. 参数包构造函数

    • 直接用参数包初始化成员数组,C++14允许在constexpr构造函数中初始化聚合类型(数组)。
    • 通过std::enable_if限制参数数量不超过ElementNum,避免传入过多参数导致编译错误。
    • 用扩展初始化列表补全剩余元素为0,确保数组所有元素都被初始化。
  2. std::array构造函数

    • 改用递归模板函数实现编译期遍历,C++14支持constexpr递归函数。
    • 通过v.data()获取数组首地址,C++14中std::array::data()的const版本是constexpr,可在编译期访问元素。
  3. C数组引用构造函数

    • 将参数改为数组引用const ElementType (&data)[ElementNum],避免数组退化为指针,编译器能在编译期确认数组的常量属性。
    • 同样用递归模板函数完成编译期赋值。

内容的提问来源于stack exchange,提问作者lei hu

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.01 16:02:11