我的插槽 class 错过了 std::function 有什么?

What does my slot class miss that std::function has?

我写了自己的 "slot" aka "callable wrapper" 因为我想在其他对象上提供成员函数槽重新绑定(即我需要一种方法来存储成员函数指针和指向 [= =29=] 有问题)。

我运行进行了一次小型测试,发现std::function在我的系统(64位Linux)上是两倍(GCC/libstdc++)到三倍( Clang/libc++) 大小与我自己实现的类似class,大小为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 的尺寸比我上面的廉价实现大?

您的 class 功能与 std::function 提供的功能有很大不同。您要求 class 的用户提供 'callable' 对象的实际类型作为模板的参数。

相反,std::function不需要这个,可以处理任何可调用对象,只要它有operator()需要的接口.尝试将您的模板与未知类型的对象一起使用,例如 std::bind 的结果,您就会明白我的意思。

由于功能差异很大,比较大小没有实际意义。

您的 function_slot 采用 Callable 和一组 args...,returns 是从 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 的对象执行此操作。

这意味着如果你这样做:

std::function<void(std::ostream&)> hello_world =
  [s = "hello world"s](std::ostream& os)
  {
    os << s;
  };
hello_world(std::cout);

它没有对 std::function 的良好实现进行动态分配。

请注意,一些主要的库供应商在这种情况下会进行动态分配。