Functor
,它的界面将如下所示:
void test(int x) {...}
struct test2{ void operator()(int x) {...} };
int main()
{
Functor<void, int> cmd1{test};
Functor<void, int> cmd2{test2{}};
Functor<void, int> cmd3{ [](int x){...} };
cmd1(2);
cmd2(3);
cmd3(77);
return 0;
}
在标准实现中,使用抽象类(纯虚)来达到类型擦除的目的。如果没有它,人们就必须以某种方式“保存”传递给 Functor
的任何类型,并在调用它时“加载”该类型。像这样的东西:
template <typename R, typename... Args>
struct Functor
{
void* _ptr;
template <class Fun>
Functor(Fun fun) : _ptr(new Holder<Functor<R, Args...>, Fun>(fun))
{ /* "save" the type of 'Fun' */}
R operator()(Args... args) {
/* "load" the type of 'Fun' */
auto tmp = static_cast< Holder<Functor<R, Args...>, Fun>* >(_ptr);
(*tmp)(x);
}
};
我认为虚拟函数实际上通过 vtable 做了类似的事情,但我希望它在编译时完成,理论上这应该是可行的,因为 Functor
在某个时刻知道可调用的类型。我设法利用
类型漏洞做到了这一点。像这样的东西(为简单起见,仅假设一个参数P
):
template <class T>
struct tag {
friend auto loophole(tag<T>);
};
template <class Parent, class F>
struct Holder
{
using ReturnType = typename Parent::ReturnType;
using ArgType = typename Parent::ArgType;
F _f;
Holder();
Holder(F f) : _f(f) {}
ReturnType operator()(ArgType x) { return _f(x); }
friend auto loophole(tag<Parent>) { return Holder<Parent, F>{}; }
};
template <class R, class P>
struct Functor
{
using ReturnType = R;
using ArgType = P;
void* _ptr;
template <class Fun>
Functor(Fun fun) : _ptr(new Holder<Functor<R, P>, Fun>(fun))
{/* 'Fun' is "saved" through template instantiation of 'Holder' */}
R operator()(P x) {
auto tmp = static_cast<decltype( loophole(tag<Functor<R, P>>{}) )*>(_ptr);
return (*tmp)(x);
}
};
对于两个具有不同 Functor
的
Fun
的情况,这会引发多重定义错误,但可以通过引入编译时生成的数字(该数字作为第二个模板参数传递给
tag
)来进一步改进该示例。所以基本上,这适用于 gcc 和 clang(没有与其他人一起测试)。然而,这种朋友注入是不受欢迎的,并且标准规定它不应该在生产中使用。比我更聪明/更有见识的人可以提供更复杂的解决方案(如果有的话)。
#include <iostream>
template <typename R, typename ...P>
struct Func
{
void *_ptr = nullptr;
R (*call)(void *, P...) = nullptr;
template <typename Fun>
Func(Fun func) :
_ptr(new Fun{func} ),
call(
[](void *_ptr, P ...params)
-> R {
auto fun_ptr = static_cast<Fun*>(_ptr);
return (*fun_ptr)(std::forward<P>(params)...);}
)
{/* 'Fun' is "saved" through function pointer 'call' */}
R operator()(P ...params) const {return call(_ptr, std::forward<P>(params)...);}
};
void test(int x) {std::cout << x << std::endl;}
int main()
{
Func<void, int> x{test};
x(42);
}
有趣的是,这会产生精确相同的程序集(至少在 gcc 13.2 上)作为具有我在原始帖子中提供的类型漏洞的解决方案(但也保证适用于所有编译器)。 这里!我觉得这很有趣。另外,至少在我的应用程序中,这给了我比 std::function
更好的性能。如果有人有其他类似的想法/经验,请分享。我想知道这是否是完全合法的唯一方法。