C++17下如何实现调用不同参数成员函数的监听器通知模板?
实现C++17下支持多参数成员函数的监听器通知模板
问题场景
你需要一个通用模板函数,遍历不同类型的监听器列表,调用它们各自参数不同的成员函数,比如IFooListener::Foo(int, double, std::string)和IBarListener::Bar(std::string, std::string),并且希望能像这样调用:
notifyListeners(fooListeners, &IFooListener::Foo, 1, 2.0, "Bla"); notifyListeners(barListeners, &IBarListener::Bar, "one", "two");
解决方案
可以利用C++17的可变参数模板和完美转发来实现,不需要依赖std::function,直接传递成员函数指针即可。同时可以添加编译期检查,确保传入的成员函数属于监听器类型。
完整实现代码如下:
#include <vector> #include <utility> #include <type_traits> // 辅助模板:编译期检查成员函数是否属于目标监听器类型 template <typename T, typename MemFunc> struct is_valid_member_function : std::false_type {}; template <typename T, typename Ret, typename... Args> struct is_valid_member_function<T, Ret(T::*)(Args...)> : std::true_type {}; template <typename T, typename Ret, typename... Args> struct is_valid_member_function<T, Ret(T::*)(Args...) const> : std::true_type {}; template <typename T, typename MemFunc, typename... Args> void notifyListeners(std::vector<T*> listeners, MemFunc memfunc, Args&&... args) { // 编译期校验:确保传入的是当前监听器类型的成员函数 static_assert(is_valid_member_function<T, MemFunc>::value, "memfunc must be a member function of the listener type"); for (auto listener : listeners) { if (listener != nullptr) { // 空指针检查,避免程序崩溃 // 调用成员函数,完美转发参数减少拷贝 (listener->*memfunc)(std::forward<Args>(args)...); } } } // 示例监听器接口类 class IFooListener { public: // 成员函数需设为public,否则外部无法调用 virtual void Foo(int, double, std::string) = 0; virtual ~IFooListener() = default; }; class IBarListener { public: virtual void Bar(std::string, std::string) = 0; virtual ~IBarListener() = default; }; // 使用示例 int main() { std::vector<IFooListener*> fooListeners; std::vector<IBarListener*> barListeners; // 假设已添加具体的监听器实例 notifyListeners(fooListeners, &IFooListener::Foo, 1, 2.0, "Bla"); notifyListeners(barListeners, &IBarListener::Bar, "one", "two"); }
核心细节说明
- 可变参数模板:
Args&&... args接收任意数量、任意类型的参数,通过std::forward实现完美转发,避免不必要的对象拷贝。 - 成员函数指针调用:使用
(listener->*memfunc)(...)的标准语法调用成员函数,这是C++中调用成员函数指针的固定写法。 - 编译期检查:通过自定义模板
is_valid_member_function,在编译阶段就验证成员函数的合法性,提前暴露错误而非运行时崩溃。 - 空指针防护:遍历监听器时判断指针是否为空,避免空指针解引用导致的程序异常。
- 访问权限:监听器的成员函数必须设为
public(接口类通常会把纯虚函数设为public),否则外部无法调用。
内容的提问来源于stack exchange,提问作者Frank
相关产品推荐
相关产品推荐

