如何将C++14 constexpr函数改写为C++11兼容版本?
适配C++11的
multi_array线性索引计算方案 你的问题核心是C11对constexpr函数的严格限制:C11中constexpr函数只能包含单一的return语句,不允许修改局部变量(你原代码里对index的多次赋值是C14才放宽的规则)。要适配C11,我们可以用模板递归实现编译期线性索引计算,完全避开局部变量修改的问题。
改写思路
把线性索引的计算拆分为递归的模板逻辑:
- 空索引列表直接返回0;
- 对第一个索引,计算它乘以后续所有维度的乘积,再加上剩余索引的线性计算结果。
整个过程完全通过编译期模板实例化完成,完美符合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, "!"); }
关键改动说明
- 新增
tail_product模板:用于计算从指定深度开始的后续维度乘积,比如对于维度2,2,深度0时返回2,深度1时返回1; - 新增
linear_index_calculator模板:通过递归函数compute_recursive逐步累加每个索引的贡献,全程无局部变量修改; - 替换原
linearized_index函数:直接调用递归模板的计算方法,完全符合C++11的constexpr函数要求。
这段代码可以在C++11标准下正常编译,同时保留了原有的编译期元素访问能力。
内容的提问来源于stack exchange,提问作者Henri Menke
相关产品推荐
相关产品推荐

