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

如何将C++14 constexpr函数改写为C++11兼容版本?

适配C++11的multi_array线性索引计算方案

你的问题核心是C11对constexpr函数的严格限制:C11中constexpr函数只能包含单一的return语句,不允许修改局部变量(你原代码里对index的多次赋值是C14才放宽的规则)。要适配C11,我们可以用模板递归实现编译期线性索引计算,完全避开局部变量修改的问题。

改写思路

把线性索引的计算拆分为递归的模板逻辑:

  1. 空索引列表直接返回0;
  2. 对第一个索引,计算它乘以后续所有维度的乘积,再加上剩余索引的线性计算结果。

整个过程完全通过编译期模板实例化完成,完美符合C++11的constexpr规则。

完整可编译代码

#include <cstddef> // std::size_t

namespace meta {
// product:计算维度乘积
template <std::size_t...> struct product;

template <std::size_t head, std::size_t... dim>
struct product<head, dim...> {
    static constexpr std::size_t const value = head * product<dim...>::value;
};

template <>
struct product<> {
    static constexpr std::size_t const value = 1;
};

// pack_element:获取指定位置的维度值
template <std::size_t index, std::size_t head, std::size_t... pack>
struct pack_element {
    static_assert(index < sizeof...(pack) + 1, "index out of bounds");
    static constexpr std::size_t const value = pack_element<index - 1, pack...>::value;
};

template <std::size_t head, std::size_t... pack>
struct pack_element<0, head, pack...> {
    static constexpr std::size_t const value = head;
};

// index_sequence:C++11兼容的索引序列实现
template <std::size_t... I>
struct index_sequence {};

template <std::size_t N, std::size_t... I>
struct make_index_sequence : public make_index_sequence<N - 1, N - 1, I...> {};

template <std::size_t... I>
struct make_index_sequence<0, I...> : public index_sequence<I...> {};

// tail_product:计算从指定位置开始的维度乘积
template <std::size_t Start, std::size_t... Dims>
struct tail_product;

template <std::size_t Start, std::size_t First, std::size_t... Rest>
struct tail_product<Start, First, Rest...> {
    static constexpr std::size_t value =
        (Start == 0) ? product<First, Rest...>::value : tail_product<Start - 1, Rest...>::value;
};

template <std::size_t Start>
struct tail_product<Start> {
    static constexpr std::size_t value = 1;
};

// linear_index_calculator:递归计算线性索引的模板
template <std::size_t... Dims>
struct linear_index_calculator {
    template <typename... Idx>
    static constexpr std::size_t compute(Idx... idx) {
        return compute_recursive(0, idx...);
    }

private:
    // 递归终止条件:无剩余索引时返回0
    static constexpr std::size_t compute_recursive(std::size_t) {
        return 0;
    }

    // 递归计算:当前索引的贡献 + 剩余索引的结果
    template <typename FirstIdx, typename... RestIdx>
    static constexpr std::size_t compute_recursive(std::size_t depth, FirstIdx first, RestIdx... rest) {
        return static_cast<std::size_t>(first) * tail_product<depth + 1, Dims...>::value +
               compute_recursive(depth + 1, rest...);
    }
};

} // namespace meta

template <typename T, std::size_t... N>
class multi_array {
    // 替换原linearized_index,使用递归模板计算
    template <typename... Idx>
    constexpr std::size_t linearized_index(Idx... idx) const {
        return meta::linear_index_calculator<N...>::compute(idx...);
    }

    // 存储数组
    T m_data[meta::product<N...>::value];

public:
    constexpr multi_array() {}

    template <typename... U>
    constexpr multi_array(U... data) : m_data{T(data)...} {}

    template <typename... Idx>
    constexpr T operator()(Idx... idx) const noexcept {
        std::size_t index = linearized_index(idx...);
        return m_data[index];
    }
};

int main() {
    constexpr multi_array<double, 2, 2> const b = {0, 0, 0, 1};
    static_assert(b(1, 1) == 1, "!");
}

关键改动说明

  1. 新增tail_product模板:用于计算从指定深度开始的后续维度乘积,比如对于维度2,2,深度0时返回2,深度1时返回1;
  2. 新增linear_index_calculator模板:通过递归函数compute_recursive逐步累加每个索引的贡献,全程无局部变量修改;
  3. 替换原linearized_index函数:直接调用递归模板的计算方法,完全符合C++11的constexpr函数要求。

这段代码可以在C++11标准下正常编译,同时保留了原有的编译期元素访问能力。

内容的提问来源于stack exchange,提问作者Henri Menke

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.28 06:42:22