如何将非静态成员函数或捕获类实例的Lambda作为类模板的模板参数?
解决类模板中使用非静态成员函数或带捕获Lambda作为模板参数的问题
你的问题核心在于原模板wrapper的参数要求是无参的普通函数指针,但非静态成员函数和带捕获的Lambda都不符合这个签名:
- 非静态成员函数隐含了一个
this指针参数,实际签名是std::string (bar::*)(),和模板期望的std::string (*)()不匹配; - 捕获外部变量(比如
this)的Lambda无法转换为普通函数指针,只有无捕获的Lambda才能做这种转换。
下面提供几种可行的修改方案:
方案1:修改模板以支持成员函数指针和实例绑定
我们可以调整wrapper模板,让它同时接受成员函数指针和类实例的类型,然后在调用时绑定实例:
#include <iostream> // 通用版本:支持成员函数指针 template<typename T, std::string (T::*Fun)()> struct member_wrapper { T* instance; member_wrapper(T* inst) : instance(inst) {} void operator()() { std::cout << "wrapped " << (instance->*Fun)() << std::endl; } }; // 保留原版本支持普通函数 template<std::string (*Fun)()> struct wrapper { void operator()() { std::cout << "wrapped " << Fun() << std::endl; } }; std::string foo() { return "foo"; } struct bar { static std::string str1() { return "bar1"; } wrapper<str1> wrapped_bar1; std::string str2() { return "bar2"; } // 使用成员函数版本的wrapper,初始化时传入this member_wrapper<bar, &bar::str2> wrapped_bar2; bar() : wrapped_bar2(this) {} }; int main() { wrapper<foo> wrapped; wrapped(); bar some_bar; some_bar.wrapped_bar1(); some_bar.wrapped_bar2(); // 现在可以正常调用 }
方案2:让模板支持任意可调用对象(更通用)
如果希望wrapper能兼容所有可调用类型(普通函数、成员函数、Lambda、仿函数等),可以把模板参数改成可调用对象的类型,然后在wrapper中存储该对象的实例:
#include <iostream> #include <functional> template<typename Callable> struct wrapper { Callable func; wrapper(Callable f) : func(std::move(f)) {} void operator()() { std::cout << "wrapped " << func() << std::endl; } }; // 辅助函数简化构造(可选) template<typename Callable> auto make_wrapper(Callable&& f) { return wrapper<std::decay_t<Callable>>(std::forward<Callable>(f)); } std::string foo() { return "foo"; } struct bar { static std::string str1() { return "bar1"; } wrapper<decltype(&bar::str1)> wrapped_bar1{&bar::str1}; std::string str2() { return "bar2"; } // 绑定this到成员函数 wrapper<std::function<std::string()>> wrapped_bar2; bar() : wrapped_bar2([this]() { return this->str2(); }) {} }; int main() { auto wrapped = make_wrapper(foo); wrapped(); bar some_bar; some_bar.wrapped_bar1(); some_bar.wrapped_bar2(); // 正常调用带捕获的Lambda绑定的成员函数 }
这个方案灵活性最高,几乎支持所有可调用类型,包括带捕获的Lambda。
方案3:使用无捕获Lambda包装成员函数(仅限特定场景)
如果你坚持要把函数作为模板参数传递,可以用无捕获的Lambda,通过将实例指针作为参数传递(但需要修改wrapper的调用逻辑):
#include <iostream> template<std::string (*Fun)(void*)> struct wrapper { void* instance; wrapper(void* inst) : instance(inst) {} void operator()() { std::cout << "wrapped " << Fun(instance) << std::endl; } }; std::string foo() { return "foo"; } struct bar { static std::string str1() { return "bar1"; } wrapper<[](void*){ return bar::str1(); }> wrapped_bar1{nullptr}; std::string str2() { return "bar2"; } // 无捕获Lambda包装成员函数,通过void*传递this static std::string str2_wrapper(void* inst) { return static_cast<bar*>(inst)->str2(); } wrapper<str2_wrapper> wrapped_bar2{this}; bar() : wrapped_bar2(this) {} }; int main() { wrapper<[](void*){ return foo(); }> wrapped{nullptr}; wrapped(); bar some_bar; some_bar.wrapped_bar1(); some_bar.wrapped_bar2(); }
这种方案比较繁琐,仅适合必须用模板参数传递函数的场景,不推荐作为通用方案。
内容的提问来源于stack exchange,提问作者auzn
相关产品推荐
相关产品推荐

