我的插槽类错过了std :: function有什么作用?

时间:2016-12-06 20:11:03

标签: c++ c++11 functional-programming std-function

我编写了自己的“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的尺寸大于上面的廉价实施?

2 个答案:

答案 0 :(得分:3)

您的function_slot需要Callable并设置args...,并返回从slot<args...>继承virtual operator()的类型。

要以多态方式将其用作值,您必须将其包装在智能指针中并将其存储在堆上,并且您必须将包装类operator()转发到slot<args...>一个。

std::function对应该包装,而不是slotcallable_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的结果,您将知道我的意思。

由于功能非常不同,因此比较尺寸是没有意义的。