C++20中如何推导ArrayND类的维度模板参数?
问题描述
我实现了一个带两个模板参数(类型T和维度数N)的N维数组类ArrayND。希望构造3维数组时直接写ArrayND<float> A(x, y, z);,而不用冗余指定维度数3(即避免ArrayND<float, 3> A(x, y, z);这种写法)。
我尝试编写了以下推导指南:
template <class T, class ...I> ArrayND(I...) -> ArrayND<T, sizeof...(I)>;
但用G++12.1.0加-std=c++20编译时,报错“error: wrong number of template arguments (1, should be 2)”。想问这个需求是否可行?如果可行,我的代码哪里错了?
类的简化实现如下:
template <class T, size_t N> class ArrayND { // 存储和维度信息 std::vector<T> storage; size_t dims[N]; public: // 构造函数:接收N个维度参数 template <class ...I> requires (sizeof...(I) == N && std::is_integral_v<std::common_type_t<I...>>) constexpr ArrayND(const I ...i): storage((i * ...)), dims{i...} {} private: // 索引转换:将多维索引转为一维存储的索引 template <decltype(N) M=N-1, class I, class ...J> constexpr const auto flattened_index(const I i, const J ...j) const { auto index = i * std::accumulate(dims+N-M, dims+N, 1, std::multiplies()); if constexpr (sizeof...(J) > 0) index += flattened_index<M-1>(j...); return index; } public: // 多维索引访问 template <class ...I> requires (sizeof...(I) == N && std::is_integral_v<std::common_type_t<I...>>) constexpr auto &operator()(const I ...i) { return storage[flattened_index(i...)]; } // 获取底层数据指针 constexpr auto data() { return storage.data(); } };
解决方案
这个需求完全可行,问题出在推导指南的写法没有匹配部分模板参数指定的场景:
原推导指南的问题
你写的推导指南是针对完全不指定模板参数的情况(比如ArrayND(x,y,z)),但你需要的是用户显式指定T、推导N的场景(ArrayND<float>(x,y,z))。原推导指南无法关联用户已经指定的T,导致编译器无法识别需要推导的是第二个模板参数N,从而报参数数量错误。正确的推导指南写法
要支持部分指定模板参数的推导,推导指南的左侧需要明确匹配“指定了T,传入构造参数”的形式:template <class T, class... I> ArrayND<T>(I...) -> ArrayND<T, sizeof...(I)>;这样当你写
ArrayND<float> A(x,y,z);时,编译器会匹配这个推导指南:T被显式指定为float,sizeof...(I)是3(对应3个构造参数),因此推导出完整的模板类型ArrayND<float, 3>,完美符合需求。验证说明
修改推导指南后,用G++12.1.0加-std=c++20编译即可正常通过,构造函数的requires约束也会自动满足(推导的N等于构造参数的数量)。
内容的提问来源于stack exchange,提问作者Michael Greenburg

