C++基于CRTP实现多维数组的线性索引计算及维度存储方案
核心问题分析
你的设计主要存在两个核心冲突点:
- 静态数组的维度是编译期常量,动态数组的维度是运行期可变值,不能共用同一套模板化的索引计算逻辑
- 你当前的CRTP派生类参数传递错误,且动态数组用tuple存可变维度的思路不成立,tuple的元素数量和值都是编译期固定的,无法满足运行期Resize的需求
修正方案
1. 新增运行期版本的索引计算函数
新增支持动态维度的ComputeIndex重载,和原有的编译期版本并行:
#include <span> // 原有的编译期版本保留 template <std::size_t, typename T> using HookTypeToPack = T; template <std::size_t ...Dims> constexpr std::size_t ComputeIndex(HookTypeToPack<Dims, std::size_t> ...multi_index) { constexpr std::size_t dims_arr[] = {Dims...}; std::size_t multi_index_arr[] = {multi_index...}; std::size_t index(0), factor(1); for(int i = 0; i < sizeof...(Dims); i++) { assert(0 <= multi_index_arr[i] && multi_index_arr[i] < dims_arr[i]); index += factor * multi_index_arr[i]; factor *= dims_arr[i]; } assert(0 <= index && index < (Dims * ...)); return index; } // 新增运行期版本 constexpr std::size_t ComputeIndex(std::span<const std::size_t> dims, std::span<const std::size_t> multi_index) { assert(dims.size() == multi_index.size()); std::size_t index = 0, factor = 1; for (std::size_t i = 0; i < dims.size(); ++i) { assert(multi_index[i] < dims[i]); index += factor * multi_index[i]; factor *= dims[i]; } return index; } template <class T, std::size_t size> constexpr auto InitStaticArray(const T& value) { std::array<T, size> arr; std::fill(arr.begin(), arr.end(), value); return arr; }
2. 重写CRTP基类的下标运算符
用C++20的if constexpr自动匹配静态/动态数组的索引计算逻辑:
template <class derived, class T> class MultiDimArray { private: constexpr derived& Derived() noexcept { return static_cast<derived&>(*this); } constexpr const derived& Derived() const noexcept { return static_cast<const derived&>(*this); } protected: constexpr MultiDimArray() = default; public: template <std::same_as<std::size_t>... Is> constexpr const T& operator()(Is... is) const { static_assert(sizeof...(Is) >= 1, "下标参数数量不能为0"); const auto idx = [&] { if constexpr (requires { derived::is_static; }) { // 静态数组分支:调用编译期索引计算 return []<std::size_t... Ds>(std::index_sequence<Ds...>, Is... is) { return ComputeIndex<Ds...>(is...); }(derived::dims_v, is...); } else { // 动态数组分支:调用运行期索引计算 assert(sizeof...(Is) == Derived().m_dims.size()); return ComputeIndex(Derived().m_dims, std::array<std::size_t, sizeof...(Is)>{is...}); } }(); return Derived().data()[idx]; } template <std::same_as<std::size_t>... Is> constexpr T& operator()(Is... is) { return const_cast<T&>(std::as_const(*this).operator()(is...)); } };
3. 修正静态多维数组实现
修正CRTP参数传递错误,新增编译期维度标识:
template <class T, std::size_t ...Dims> class StaticMultiDimArray : public std::array<T, (Dims * ...)>, public MultiDimArray<StaticMultiDimArray<T, Dims...>, T> { private: constexpr static std::size_t n_entries = (Dims * ...); friend MultiDimArray<StaticMultiDimArray<T, Dims...>, T>; public: // 编译期标识和维度导出 constexpr static bool is_static = true; constexpr static auto dims_v = std::index_sequence<Dims...>{}; constexpr StaticMultiDimArray() : std::array<T, n_entries>(InitStaticArray<T, n_entries>(T{})) {} };
4. 修正动态多维数组实现
用std::vector<std::size_t>存储运行期维度,完善Resize逻辑:
template <class T> class DynamicMultiDimArray : public std::vector<T>, public MultiDimArray<DynamicMultiDimArray<T>, T> { private: std::size_t n_entries = 0; std::vector<std::size_t> m_dims; friend MultiDimArray<DynamicMultiDimArray<T>, T>; public: DynamicMultiDimArray() = default; template <typename... Dims> requires (std::same_as<Dims, std::size_t> && ...) DynamicMultiDimArray(Dims... dimensions) : std::vector<T>((dimensions * ...), T{}) { Resize(dimensions...); } template <typename... Dims> requires (std::same_as<Dims, std::size_t> && ...) void Resize(Dims... dimensions) { m_dims = {dimensions...}; n_entries = (dimensions * ...); this->resize(n_entries, T{}); } };
使用示例
int main() { // 静态数组测试 2x3 StaticMultiDimArray<int, 2, 3> static_arr; static_arr(0, 0) = 1; static_arr(1, 2) = 5; assert(static_arr(0, 0) == 1); assert(static_arr(1, 2) == 5); // 动态数组测试 3x4 DynamicMultiDimArray<int> dynamic_arr(3U, 4U); dynamic_arr(2U, 3U) = 10; assert(dynamic_arr(2U, 3U) == 10); // 运行期Resize为2x2 dynamic_arr.Resize(2U, 2U); dynamic_arr(1U,1U) = 7; assert(dynamic_arr(1U,1U) == 7); }
额外优化建议
不推荐公开继承std::array和std::vector,标准容器没有虚析构函数,公开继承容易出现内存泄漏风险,改为组合形式更安全。
内容的提问来源于stack exchange,提问作者niran90
相关产品推荐
相关产品推荐

