如何泛化C++模板类pack支持任意数量可调用对象及类型名调用?
问题描述
给定以下简化版C++模板类pack,其构造函数接收两个可调用对象并将它们作为成员存储:
#include <iostream> struct callable1{ void operator()(){ std::cout << "callable 1\n"; } }; struct callable2{ void operator()(){ std::cout << "callable 2\n"; } }; template<typename T1, typename T2> class pack{ public: pack(T1 t1, T2 t2): f1{t1}, f2{t2} {} T1 f1; T2 f2; }; int main(int, char**) { pack<callable1, callable2> p{callable1{}, callable2{}}; p.f1(); p.f2(); return 0; }
运行后输出:
callable 1 callable 2
需要解决两个核心需求:
- 泛化
pack类,使其能接收**任意数量(至少一个)**的可调用对象,且传入两个对象时行为与原代码一致(禁止使用宏)。 - 调用成员函数时,无需通过
p.f1()、p.f2()的方式,而是直接使用可调用对象的类型名调用,例如p.callable1()、p.callable2()。
解决方案
我们可以利用可变参数模板和基类继承展开的技巧实现需求。核心思路是为每个可调用类型创建辅助基类,持有对应实例并提供调用入口;再让pack继承所有辅助基类,通过模板函数实现类型名调用的转发逻辑。
完整实现代码
#include <iostream> #include <utility> // 辅助基类:持有单个可调用对象,提供函数符调用入口 template<typename Callable> struct CallableWrapper { explicit CallableWrapper(Callable c) : callable(std::move(c)) {} // 内部调用逻辑 auto invoke() -> decltype(auto) { return callable(); } private: Callable callable; }; // 可变参数模板的pack类,继承所有CallableWrapper基类 template<typename... Callables> class pack : public CallableWrapper<Callables>... { public: // 确保至少传入一个可调用对象 static_assert(sizeof...(Callables) > 0, "pack must contain at least one callable"); // 可变参数构造函数,初始化所有基类实例 explicit pack(Callables... callables) : CallableWrapper<Callables>(std::move(callables))... {} // 模板转发函数:通过类型名匹配对应的CallableWrapper基类 template<typename Callable> auto Callable() -> decltype(auto) { return static_cast<CallableWrapper<Callable>&>(*this).invoke(); } }; // 测试用可调用类型 struct callable1{ void operator()(){ std::cout << "callable 1\n"; } }; struct callable2{ void operator()(){ std::cout << "callable 2\n"; } }; struct callable3{ void operator()(){ std::cout << "callable 3\n"; } }; int main() { // 测试3个可调用对象的场景 pack<callable1, callable2, callable3> p{callable1{}, callable2{}, callable3{}}; p.callable1(); p.callable2(); p.callable3(); // 测试2个可调用对象的场景,与原代码行为兼容 pack<callable1, callable2> p2{callable1{}, callable2{}}; p2.callable1(); p2.callable2(); return 0; }
关键逻辑解释
CallableWrapper辅助类:每个可调用类型对应一个独立的CallableWrapper实例,负责持有对象并封装调用逻辑,避免pack类内部成员命名冲突。- 可变参数继承:
pack通过public CallableWrapper<Callables>...语法,将所有辅助基类的成员纳入自身作用域,支持任意数量的可调用对象扩展。 - 类型名调用实现:
pack中的模板成员函数Callable()通过static_cast定位到对应类型的CallableWrapper基类,调用其invoke()方法,实现p.callable1()这类直观调用。 - 非空参数校验:通过
static_assert确保模板参数包至少包含一个可调用对象,符合需求中的“至少一个”限制。
运行输出
callable 1 callable 2 callable 3 callable 1 callable 2
内容的提问来源于stack exchange,提问作者user17206068
相关产品推荐
相关产品推荐

