泛型成员函数指针作为模板参数
Posted
技术标签:
【中文标题】泛型成员函数指针作为模板参数【英文标题】:generic member function pointer as a template parameter 【发布时间】:2012-04-04 11:10:43 【问题描述】:考虑这段代码:
#include <iostream>
using namespace std;
class hello
public:
void f()
cout<<"f"<<endl;
virtual void ff()
cout<<"ff"<<endl;
;
#define call_mem_fn(object, ptr) ((object).*(ptr))
template<R (C::*ptr_to_mem)(Args...)> void proxycall(C& obj)
cout<<"hello"<<endl;
call_mem_fn(obj, ptr_to_mem)();
int main()
hello obj;
proxycall<&hello::f>(obj);
当然这不会在第 16 行编译,因为编译器不知道 R
、C
和 Args
是什么。但是还有另一个问题:如果试图在ptr_to_mem
之前定义那些模板参数,他就会遇到这种糟糕的情况:
template<typename R, typename C, typename... Args, R (C::*ptr_to_mem)(Args...)>
// ^variadic template, but not as last parameter!
void proxycall(C& obj)
cout<<"hello"<<endl;
call_mem_fn(obj, ptr_to_mem)();
int main()
hello obj;
proxycall<void, hello, &hello::f>(obj);
令人惊讶的是,g++ 并没有抱怨Args
不是模板列表中的最后一个参数,但无论如何它不能将proxycall
绑定到正确的模板函数,只是指出它是一个可能的候选者。
有什么解决办法吗?我最后的手段是将成员函数指针作为参数传递,但如果我可以将它作为模板参数传递,它将更适合我的其余代码。
编辑: 正如一些人指出的那样,这个例子似乎毫无意义,因为 proxycall 不会传递任何参数。这在我正在处理的实际代码中并非如此:参数是通过 Lua 堆栈中的一些模板技巧获取的。但那部分代码与问题无关,比较冗长,这里就不贴了。
【问题讨论】:
在我看来,在这种情况下,您实际上并不需要可变参数模板参数。proxycall()
不会将任何参数传递给成员函数指针调用,因此使用可变参数模板参数似乎使问题变得比它需要的更困难。
这没有意义。您的“call_mem_fn”#define 实际上并没有提供参数。因此,如果 Args 不是空的,它将不起作用。那么您如何期望它实际发挥作用呢?
问题中的代码只是一个例子。实际代码将处理具有任意数量参数的函数,并且它们将从其他地方(即 Lua 堆栈)检索。获取参数的元编程胶水代码已经在工作,我不会在这里粘贴它,因为它很长。
我想试一试,但它认为我在中途的某个地方迷路了,所以如果它关闭,请忽略答案。
FTR 可变参数包不必最后声明,但只能推导出来,而不是由<...>
的用户明确指定。
【参考方案1】:
你可以试试这样的:
template <typename T, typename R, typename ...Args>
R proxycall(T & obj, R (T::*mf)(Args...), Args &&... args)
return (obj.*mf)(std::forward<Args>(args)...);
用法:proxycall(obj, &hello::f);
或者,要使 PTMF 成为模板参数,请尝试特化:
template <typename T, T> struct proxy;
template <typename T, typename R, typename ...Args, R (T::*mf)(Args...)>
struct proxy<R (T::*)(Args...), mf>
static R call(T & obj, Args &&... args)
return (obj.*mf)(std::forward<Args>(args)...);
;
用法:
hello obj;
proxy<void(hello::*)(), &hello::f>::call(obj);
// or
typedef proxy<void(hello::*)(), &hello::f> hello_proxy;
hello_proxy::call(obj);
【讨论】:
我总是忘记,当我遇到函数模板的麻烦时,类模板就是我的救生艇。 别忘了处理const
成员函数。 (但如果你决定不打扰volatile
或const volatile
,我不会抱怨。)
@user1810087: const 限定的成员函数指针类型是R (T::*)(Args...) const
。
据我了解,template <typename T, T> struct proxy
在 C++17 中可以简化为 template <auto> struct proxy
。至少对我有用。这极大地简化了使用,特别是对于具有复杂签名的成员函数!
@nilo 感谢您指出auto
,这似乎是address exactly that。否则,如果没有此功能,您可以使用decltype(hello::f)
来摆脱第一个模板参数的签名复杂性。【参考方案2】:
在现代 C++ 中,可以使用 template<auto>
和通用 lambda-wrapper:
#include <utility>
#include <functional>
template<auto mf, typename T>
auto make_proxy(T && obj)
return [&obj] (auto &&... args) return (std::forward<T>(obj).*mf)(std::forward<decltype(args)>(args)...); ;
struct R ;
struct A ;
struct B ;
struct Foo
R f(A &&, const B &) return ;
//R f(A &&, const B &) const return ;
;
int main()
Foo foo;
make_proxy<&Foo::f>(foo)(A, B);
//make_proxy<static_cast<R (Foo::*)(A &&, const B &) const>(&Foo::f)>(std::as_const(foo))(A, B);
//make_proxy<static_cast<R (Foo::*)(A &&, const B &)>(&Foo::f)>(foo)(A, B);
如果有重载,应该像注释代码那样明确指定成员函数类型。
【讨论】:
以上是关于泛型成员函数指针作为模板参数的主要内容,如果未能解决你的问题,请参考以下文章