有一些C ++“委托”的提案,其开销低于boost::function
:
是否已将这些想法用于实施std::function
,从而产生比boost::function
更好的效果?有人比较过std::function
与boost::function
的效果吗?
我想知道这个特别针对英特尔64位架构上的GCC编译器和libstdc ++ ,但欢迎其他编译器的信息(例如Clang)。
答案 0 :(得分:28)
在libstdc ++的std::function
中,我们使用一个适当大小和对齐的联合类型来存储指针,函数指针或指向成员函数的指针。我们避免为任何可以存储在该大小和对齐中的函数对象进行堆分配,但仅当它是“位置不变”时
/**
* Trait identifying "location-invariant" types, meaning that the
* address of the object (or any of its members) will not escape.
* Also implies a trivial copy constructor and assignment operator.
*/
代码基于std::tr1::function
实现,而且该部分没有显着变化。我认为可以使用std::aligned_storage
简化,并且可以通过专门化特征来改进,以便将更多类型识别为位置不变。
在没有任何虚函数调用的情况下调用目标对象,通过在std::function
中存储单个函数指针来完成类型擦除,boost::function
是函数模板特化的地址。所有操作都是通过存储的指针调用该函数模板并传入一个枚举来识别它被要求执行的操作来完成的。这意味着没有vtable,只需要在对象中存储单个函数指针。
这个设计是由最初的std::function
作者提供的,我相信它接近于提升实施。有关基本原理,请参阅Boost.Function的Performance文档。这意味着GCC的boost::function
不太可能比std::function
更快,因为它是同一个人的类似设计。
N.B。我们的new
不支持使用分配器构建,它需要做的任何分配都将使用std::function
完成。
为了回应Emile的评论,表达了避免为struct A {
int i = 0;
int foo() const { return 0; }
};
struct InvokeA
{
int operator()() const { return a->foo(); }
A* a;
};
namespace std
{
template<> struct __is_location_invariant<InvokeA>
{ static const bool value = true; };
}
int main()
{
A a;
InvokeA inv{ &a };
std::function<int()> f2(inv);
return f2();
}
保留指向成员函数和对象的指针的堆分配的愿望,这里有点骇客(但你没有听到它我; - )
InvokeA
诀窍是function
足够小以适应function
的小对象缓冲区,并且特征专门化说它可以安全存储在那里,所以a
成立该对象的副本直接,而不是在堆上。只要指向它的指针仍然存在,这就要求function
保持不变,但如果bind(&A::foo, &a)
的目标是{{1}},情况就是如此。
答案 1 :(得分:4)
正如评论中所指出的,std :: function只是一个接口,不同的实现可能会做不同的事情,但值得注意的是标准确实对此事有所说明。从20.8.11.2.1 / 5开始(看起来更像是IP地址而不是标准的一部分):
注意:鼓励实施以避免动态使用 为小型可调用对象分配内存,例如f target是仅包含对象的指针或引用的对象 和一个成员函数指针。 -end note
这是鼓励实施者采用“小功能优化”的标准方法,这是由引用的代表文章推动的。 (文章本身实际上并没有谈论.NET意义上的委托。相反,他们使用术语“委托”来表示绑定的成员函数。)