我编写了自己的“slot”又称“可调用包装器”,因为我想在其他对象上提供成员函数槽重新绑定(即我需要一种方法来存储成员函数指针和指向所讨论的类的指针)。 / p>
我运行了一个小规模的测试并发现我的系统上的std::function
(64位Linux)是两次(GCC / libstdc ++)三次(Clang / libc ++)我自己实现类似类的大小,大小为16个字节。非成员函数和lambda的实现是这样的(const void*
第一个参数是为了统一,这里没有显示成员函数槽):
template<typename... ArgTypes>
class slot
{
public:
virtual ~slot() = default;
virtual void operator()(const void* object, ArgTypes...) const = 0;
protected:
slot() = default;
};
template<typename Callable, typename... ArgTypes>
class callable_slot : public slot<ArgTypes...>
{
public:
callable_slot(Callable function_pointer_or_lambda) : callable(function_pointer_or_lambda) {}
virtual void operator()(const void*, ArgTypes... args) const override { callable(args...); }
private:
Callable callable;
};
template<typename Callable>
class callable_slot<Callable> : public slot<>
{
public:
callable_slot(Callable function_pointer_or_lambda) : callable(function_pointer_or_lambda) {}
virtual void operator()(const void*) const override { callable(); }
private:
Callable callable;
};
template<typename Callable, typename... ArgTypes>
using function_slot = callable_slot<Callable, ArgTypes...>;
我理解target
之类的内容在这里没有实现,但我认为任何缺少的函数都不会增加对象的大小。
我要问的是:为什么std::function
的尺寸大于上面的廉价实施?
答案 0 :(得分:3)
您的function_slot
需要Callable
并设置args...
,并返回从slot<args...>
继承virtual operator()
的类型。
要以多态方式将其用作值,您必须将其包装在智能指针中并将其存储在堆上,并且您必须将包装类operator()
转发到slot<args...>
一个。
std::function
对应该包装,而不是slot
或callable_slot
对象。
template<class...Args>
struct smart_slot {
template<class Callable> // add SFINAE tests here TODO! IMPORTANT!
smart_slot( Callable other ):
my_slot( std::make_unique<callable_slot<Callable, Args...>>( std::move(other) ) )
{}
void operator()( Args...args ) const {
return (*my_slot)(std::forward<Args>(args)...);
}
// etc
private:
std::unique_ptr<slot<Args...>> my_slot;
};
smart_slot
比您的代码更接近std::function
。就std::function
而言,您撰写的所有内容都是std::function
用户无法看到的实施细节。
现在,这只需要std::function
是一个指针的大小。 std::function
更大,因为它具有所谓的小对象优化。
它不仅仅存储智能指针,而且还有一块内存。如果传入的对象适合该内存块,则它就构造它而不是进行堆分配。
std::function
基本上是强制执行此操作的简单情况,如传递函数指针。质量实现为更大和更复杂的对象执行。 MSVC为最大为std::string
s的对象执行此操作。
这意味着如果你这样做:
std::function<void(std::ostream&)> hello_world =
[s = "hello world"s](std::ostream& os)
{
os << s;
};
hello_world(std::cout);
它没有对std::function
的体面实施进行动态分配。
请注意,在这种情况下,一些主要的库供应商会进行动态分配。
答案 1 :(得分:0)
您的课程功能与std::function
提供的功能完全不同。您要求类的用户提供实际类型的“可调用”对象作为模板的参数。
相反,std::function
不需要这个,并且可以处理任何可调用对象,只要它具有所需接口的operator()
即可。尝试将模板与未知类型的对象一起使用,例如std::bind
的结果,您将知道我的意思。
由于功能非常不同,因此比较尺寸是没有意义的。