C++如何以模板类实例为模板参数创建可变参数类
实现方案
完全可以通过类实现同等效果,核心根据你使用的C++标准版本选择对应实现即可。
你期望的B<A<int>(1), A<float>(2.4f), A<bool>(false)>()实例化写法,本质是把A类的临时实例作为非类型模板参数传入,C++20标准对类类型非类型模板参数做了正式支持,可以完全匹配你要的写法。
C++20 完美匹配需求的实现
首先你定义的A类需要做小调整,构造函数加constexpr修饰,满足编译期常量要求:
#include <tuple> #include <iostream> #include <utility> template<typename T> class A { public: T value; constexpr A(T v) : value(v) {} };
类B用auto声明的非类型参数包接收所有传入的A实例,用tuple存储所有成员:
template<auto... Args> class B { private: std::tuple<decltype(Args)...> members{Args...}; public: // 示例:遍历打印所有持有的A实例的值 void print_values() { std::apply([](const auto&... elem) { ((std::cout << elem.value << '\n'), ...); }, members); } };
使用方式完全符合你的预期:
int main() { B<A<int>(1), A<float>(2.4f), A<bool>(false)> b; b.print_values(); return 0; }
注意点:
- 作为非类型模板参数传入的A实例必须是编译期常量,因此A的构造函数必须是constexpr的
- C++20要求作为非类型模板参数的类类型满足结构性条件:不能有私有/保护非静态成员、不能有虚函数、不能有虚基类,你的A类只要保持简单聚合类或者公开接口的constexpr构造即可满足要求
- 所有持有的实例都存在tuple成员中,你可以自行添加访问、修改、遍历的逻辑
旧标准兼容方案(C11/C17)
如果你的编译环境不支持C++20,无法直接传递类类型的非类型模板参数,就不能把A实例直接写在模板尖括号里,可以用两种类型安全的替代方案:
C++11 版本:分离类型和构造参数
template<typename... Ts> class B { private: std::tuple<A<Ts>...> members; public: template<typename... InitArgs> B(InitArgs&&... args) : members(A<Ts>(std::forward<InitArgs>(args))...) {} }; // 实例化方式:模板参数传所有A的类型,构造函数传对应初始化值 B<int, float, bool> b(1, 2.4f, false);
C++17 版本:用CTAD简化实例化
借助C++17的类模板实参推导特性,可以做到和你写的test函数几乎一致的使用体验:
template<typename... Ts> class B { private: std::tuple<A<Ts>...> members; public: B(A<Ts>... args) : members(std::move(args)...) {} }; // 直接在构造函数传入A实例,编译器自动推导模板参数 B b(A<int>(1), A<float>(2.4f), A<bool>(false));
补充说明
你之前实现的test函数用的是C风格可变参数语法,并不是类型安全的:第一个参数会匹配为A<C>类型,后续参数属于无类型的可变参数列表,传入非A类型的值也不会触发编译错误,上面给出的所有类模板实现都是严格类型约束的,传入不符合要求的参数会直接编译失败。
内容的提问来源于stack exchange,提问作者stgr2gpns
相关产品推荐
相关产品推荐

