Ответ 1
В libstdС++ 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
, который является адресом специализированной функции. Все операции выполняются путем вызова этого шаблона функции через сохраненный указатель и передачи в перечислении, определяющем, какую операцию он просит выполнить. Это означает, что нет vtable, и в объекте должен храниться только один указатель на функцию.
Этот дизайн был внесен оригинальным автором boost::function
, и я считаю, что он близок к ускоренной реализации. См. Производительность для Boost.Function для некоторого обоснования. Это означает, что вряд ли GCC std::function
будет быстрее, чем boost::function
, потому что это аналогичный дизайн того же человека.
N.B. наш std::function
не поддерживает конструкцию с помощью распределителя, любые распределения, которые необходимо выполнить, будут выполняться с помощью new
.
В ответ на комментарий Эмиля, выражающий желание избежать выделения кучи для std::function
, который содержит указатель на функцию-член и объект, здесь немного взломать его (но вы не слышали его от меня; -)
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)
.