C++ std::function 没有虚函数?

问题描述 投票:0回答:1
是否可以创建与 cpp std::function 等效但不使用虚函数的东西?通过“等效”,我的意思是它接受任何可调用的(函数、函数指针、函子、lambda...),即使用户不知道其类型(或者如果没有类型,也可以使用运算符.* 和运算符->*) 的结果。例如,如果它被命名为

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(没有与其他人一起测试)。

然而,这种朋友注入是不受欢迎的,并且标准规定它不应该在生产中使用。比我更聪明/更有见识的人可以提供更复杂的解决方案(如果有的话)。

c++ std-function type-erasure vtable type-deduction
1个回答
0
投票
感谢@HolyBlackCat,我找到了一种没有类型漏洞的方法。重复他的解决方案(通过更正以按值而不是按通用引用传递可调用):

#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

 更好的性能。如果有人有其他类似的想法/经验,请分享。我想知道这是否是完全合法的唯一方法。

© www.soinside.com 2019 - 2024. All rights reserved.