C++可变参数模板如何实现嵌套initializer_list模板实参推导以适配ndarray?
你遇到的核心矛盾是C++标准规定无类型的花括号初始化列表(braced-init-list)无法参与模板参数推导,因此你现有的泛型构造函数参数包无法匹配{1,2}这类输入,即便显式指定了类模板参数也会因为构造函数模板参数推导失败报错。以下是两种经过验证的可行实现方案:
方案1:直接使用聚合初始化(最简洁,完全避开构造函数推导问题)
把ndarray设计为聚合类型,底层用嵌套std::array存储,不需要自定义构造函数,天然支持多层花括号初始化,且完全constexpr友好:
#include <array> #include <concepts> template <typename T, std::size_t... DIMS> struct ndarray; // 多维特化 template <typename T, std::size_t DIM, std::size_t... DIMS> struct ndarray<T, DIM, DIMS...> { std::array<ndarray<T, DIMS...>, DIM> storage; // 可按需添加operator[]、迭代器、size等接口,此处省略 constexpr std::size_t size() const { return DIM; } }; // 一维特化(叶子节点) template <typename T, std::size_t DIM> struct ndarray<T, DIM> { std::array<T, DIM> storage; constexpr std::size_t size() const { return DIM; } }; // 推导指引:支持自动推导维度 // 推导一维数组 template <typename T, std::same_as<T>... Ts> ndarray(T, Ts...) -> ndarray<T, 1 + sizeof...(Ts)>; // 推导多维数组 template <typename T, std::size_t... DIMS, std::same_as<ndarray<T, DIMS...>>... Ts> ndarray(ndarray<T, DIMS...>, Ts...) -> ndarray<T, 1 + sizeof...(Ts), DIMS...>;
这种写法下你原本报错的用例可以直接编译通过(GCC 10+、Clang 12+均支持):
ndarray a{1, 2}; // 自动推导为ndarray<int, 2> ndarray<int, 2> b{3, 4}; ndarray c{a, b}; // 自动推导为ndarray<int, 2, 2> ndarray<int, 2, 2> d{{1, 2}, {3, 4}}; // 编译正常
方案2:自定义构造函数结合非推导上下文
如果需要封装存储、不希望把类暴露为聚合类型,可以用std::type_identity_t把构造函数参数包设置为非推导上下文,避免编译器尝试从花括号初始化列表推导参数类型:
#include <array> #include <concepts> #include <type_traits> template <typename T, std::size_t... DIMS> class ndarray; template <typename T, std::size_t DIM, std::size_t... DIMS> class ndarray<T, DIM, DIMS...> { std::array<ndarray<T, DIMS...>, DIM> storage; public: // 接受多个子ndarray的构造函数,std::type_identity_t关闭参数推导 template <std::same_as<ndarray<T, DIMS...>>... Ts> constexpr ndarray(std::type_identity_t<Ts>&&... elements) : storage{std::forward<Ts>(elements)...} {} }; template <typename T, std::size_t DIM> class ndarray<T, DIM> { std::array<T, DIM> storage; public: // 一维数组接受多个T类型元素的构造函数 template <std::same_as<T>... Ts> constexpr ndarray(std::type_identity_t<Ts>&&... elements) : storage{std::forward<Ts>(elements)...} {} }; // 补充和方案1一致的推导指引即可支持自动维度推导
这种写法同样支持你需要的初始化语法,且不会引入std::initializer_list的constexpr限制。
内容的提问来源于stack exchange,提问作者QuaternionsRock
相关产品推荐
相关产品推荐

