如何让Vec<C>通过初始化列表推导模板参数为std::array<T,N>?
问题:让
Vec<C>支持花括号列表推导为std::array<T,N> 我定义了如下的Vec<C>类,需要编写构造函数和/或推导指南,使得花括号包裹的初始化列表能将模板参数C推导为std::array<T,N>。由于无需提前知晓大小,std::vector<T>可以正常适配该类。
#include <cassert> #include <iostream> #include <array> #include <vector> #include <concepts> #include <type_traits> template<typename T> concept has_resize = requires(T t) { {t.resize(0)}; }; template<typename C> class Vec { C elems; public: template<typename IndexType> requires std::integral<IndexType> && std::is_unsigned_v<IndexType> auto operator[](IndexType i) const { return elems[i]; } auto size() const { return elems.size(); } template<typename T> Vec(std::initializer_list<T> init)//MEMBER INITIALIZER LIST? { // WHAT CODE GOES HERE? } Vec(auto init) { if constexpr (has_resize<decltype(elems)>) elems.resize(init.size()); for (decltype(init.size()) i = 0; i<init.size() ; i++) elems[i] = init[i]; } }; template<typename C> Vec(C) -> Vec<C>; //WHAT'S THE CORRECT DEDUCTION GUIDE? int main() { Vec v0({1, 4, 7}); Vec v1(std::array<int,3>{2, 5, 8}); Vec v2(std::vector<double>{3, 6, 9}); }
编辑1:不能修改Vec v0({1, 4, 7});或Vec v0{1, 4, 7};这类调用方式。
编辑2:template<typename T> Vec(const std::initializer_list<T>& vec) -> Vec<std::array<T, vec.size()>>;无法正常工作。
编辑3:本问题与Stack Overflow上的“Template class and automatic deduction”相关,可作为推导指南用法的补充示例。
解决方案
核心问题是std::initializer_list的size是运行时属性,无法用于推导std::array的编译期大小N。因此需要用模板参数包捕获初始化列表元素,在编译期确定元素数量,进而推导std::array类型。
修改后的完整代码
#include <cassert> #include <iostream> #include <array> #include <vector> #include <concepts> #include <type_traits> template<typename T> concept has_resize = requires(T t) { {t.resize(0)}; }; template<typename C> class Vec { C elems; public: template<typename IndexType> requires std::integral<IndexType> && std::is_unsigned_v<IndexType> auto operator[](IndexType i) const { return elems[i]; } auto size() const { return elems.size(); } // 处理花括号初始化:用模板参数包接收元素 template<typename... Ts> requires (std::same_as<Ts, std::common_type_t<Ts...>> && ...) Vec(Ts&&... args) : elems{std::forward<Ts>(args)...} {} // 处理已有容器(如std::array、std::vector),排除初始化列表避免冲突 template<typename Container> requires (!std::is_initializer_list_v<std::decay_t<Container>>) Vec(Container&& init) { if constexpr (has_resize<decltype(elems)>) elems.resize(init.size()); for (decltype(init.size()) i = 0; i < init.size(); i++) elems[i] = init[i]; } }; // 推导指南:从参数包推导为std::array,编译期确定大小 template<typename... Ts> Vec(Ts&&...) -> Vec<std::array<std::common_type_t<Ts...>, sizeof...(Ts)>>; // 推导指南:从已有容器推导为对应容器类型 template<typename Container> Vec(Container&&) -> Vec<std::decay_t<Container>>; int main() { Vec v0({1, 4, 7}); // 推导为Vec<std::array<int, 3>> Vec v1(std::array<int,3>{2, 5, 8}); // 推导为Vec<std::array<int,3>> Vec v2(std::vector<double>{3, 6, 9}); // 推导为Vec<std::vector<double>> // 验证类型正确性 static_assert(std::same_as<decltype(v0), Vec<std::array<int,3>>>); static_assert(std::same_as<decltype(v1), Vec<std::array<int,3>>>); static_assert(std::same_as<decltype(v2), Vec<std::vector<double>>>); // 验证功能正常 std::cout << v0.size() << " " << v1.size() << " " << v2.size() << "\n"; // 输出3 3 3 std::cout << v0[0] << " " << v1[1] << " " << v2[2] << "\n"; // 输出1 5 9 }
关键要点
- 模板参数包构造函数:直接用参数包初始化内部
elems,requires约束确保所有元素类型一致,避免异构类型错误。 - 编译期推导std::array大小:推导指南中用
sizeof...(Ts)获取编译期元素数量,std::common_type_t处理类型统一(如混合int和long时自动兼容)。 - 避免构造函数冲突:给容器构造函数添加
!std::is_initializer_list_v约束,确保花括号初始化走参数包分支,容器初始化走对应分支。 - 兼容原有容器推导:保留容器类型的推导指南,确保
std::array和std::vector能正常推导为对应的Vec<C>类型。
内容的提问来源于stack exchange,提问作者Astor
相关产品推荐
相关产品推荐

