【问题标题】:Sizeof of std::function<void(int&)> typestd::function<void(int&)> 类型的大小
【发布时间】:2012-11-10 07:25:14
【问题描述】:

来自 C++11 的 std::function&lt;void(int&amp;)&gt; 和它的 sizeof = 32 有什么魔力?如果我将函数引用存储为指针,则只需 8 bytes(在 64 位机器上)。

【问题讨论】:

  • 可能是小函子优化。
  • 首先考虑成员函数指针的大小。
  • @GManNickG 是的,这就是我所要求的,即内部结构。这些 STL 文件非常不可读,所以我想知道是否有人确切地知道它。
  • 我认为不能合理地声称 std::function 是或曾经是 STL 的一部分。
  • @LightnessRacesinOrbit 哇。这些天你变得温和了:)

标签: c++ gcc stl c++11


【解决方案1】:

对于std::function&lt;Signature&gt;,这是一个有趣的对象大小和分配之间的权衡:对于小型函数对象,最好避免分配。另一方面,这样做会增加对象的大小。为了使小函数优化有用并且在实际调用对象时不引入开销,对象大小需要至少是两个指针加上一些内存来存储简单的函数对象。似乎 32 字节的大小正是这个(在sizeof(T*) 为 8 的系统上)。

也就是说,std::function&lt;Signature&gt; 对象内部存储了继承层次结构:基类提供要调用的接口,委托给实现调用接口的模板化派生(可能还有一些 clone() 功能)。在针对大小优化的实现中,函数对象只会保留一个指向基址的指针,但为了避免分配,它会留出一些内部存储空间来分配整个对象并指向该对象(内部指针在实际调用时避免了一个条件函数对象)。该对象所需的内存是一个虚函数指针加上用于初始化std::function&lt;Signature&gt; 对象的实际函数对象的任何数据。为了让成员函数与它们的对象相适应,似乎再多两个词就相当小了。

【讨论】:

  • @LightnessRacesinOrbit:一个指向基址的指针和一个虚函数指针。另外,实际函数对象的数据。
  • 我不明白虚函数指针是从哪里来的,但我先不说。
  • 在内部,std::function&lt;R(A...)&gt; 使用带有虚函数的基类,例如 struct FB { virtual R call(A...) = 0; virtual ~FB() {} };,并且在将函数对象分配给对象时,它会创建相应的派生类。
  • 查看实际实现,似乎 libstdc++ 使用运行时调度来存储指针,以防对象嵌入到实际函数中。 libcxx 使用上述更普通的实现。
【解决方案2】:

std::function 确实很神奇,因为它可以从 any 可调用对象构造!它会很乐意为你存储任何数量的状态,即使在表面上你只保留了一个微不足道的 void(int&amp;) 签名。

这种魔法肯定是有代价的,它通常在内部使用某种类型的擦除(即动态分配和虚拟调度)。细节各不相同,但生成的对象肯定是“沉重的”——这就是为什么应该避免使用 auto 和模板,只要可行!

【讨论】:

  • 如何在std::function 对象中存储状态?我以为这个std::function只是一种“安全的C++函数指针”,其实更多吧?
  • @Martin:还有很多——对于初学者来说,std::function 是一个模板,而不是一个类。而且确实充满了魔力。大量模板化构造函数和 SFINAE 等。如果你想了解的话,请查看源代码。
  • @Martin 如果我有struct big { void operator()() {}; char dummy[N]; };,那么我可以在std::function&lt;void()&gt; f = big {}; 中存储任意数量的字节。
  • @LucDanton 哦,我明白了。就是这么灵活。谢谢。
猜你喜欢
  • 2022-10-31
  • 1970-01-01
  • 2014-12-11
  • 2021-09-24
  • 2023-02-07
  • 2018-02-26
  • 2013-02-03
  • 2019-02-20
  • 2019-06-05
相关资源
最近更新 更多