En libstdC++ 's std::function
utilizamos un tipo de unión que está convenientemente dimensionado y alineado para almacenar punteros, punteros a funciones o punteros a funciones de miembros. Evitamos una asignación del montón para cualquier objeto de función que pueden almacenarse en que el tamaño y la alineación, pero sólo si es "ubicación invariante"
/**
* 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.
*/
El código se basa en la aplicación std::tr1::function
y esa parte hasn' t cambiado significativamente. Creo que podría simplificarse usando std::aligned_storage
y podría mejorarse especializando el rasgo para que se identifiquen más tipos como invariantes de ubicación.
La invocación del objeto de destino se realiza sin llamadas a funciones virtuales, el borrado de tipo se realiza almacenando un único puntero de función en el std::function
que es la dirección de una especialización de plantilla de función. Todas las operaciones se realizan llamando a esa plantilla de función a través del puntero almacenado y pasando una enumeración que identifica qué operación se le pide que realice. Esto significa que no hay vtable y solo se necesita almacenar un único puntero de función en el objeto.
Este diseño fue contribuido por el autor original boost::function
y creo que está cerca de la implementación de impulso. Consulte el documento Performance para la función Boost.Function por algún motivo. Eso significa que es bastante improbable que el std::function
de GCC sea más rápido que boost::function
, porque es un diseño similar de la misma persona.
N.B. nuestro std::function
no admite la construcción con un asignador aún, las asignaciones que necesite hacer se realizarán usando new
.
En respuesta al comentario de Emile expresan el deseo de evitar una asignación del montón por un std::function
que mantiene un puntero a la función miembro y un objeto, aquí hay un pequeño truco para hacerlo (pero no lo oyó desde me ;-)
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();
}
el truco es que InvokeA
es lo suficientemente pequeño como para caber en una pequeña memoria intermedia de objetos del function
's, y la especialización rasgo dice que es seguro para almacenar allí, por lo que el function
mantiene una copia de esa objeto directamente, no en el montón.Esto requiere a
a persistir mientras el puntero a ella persiste, sino que sería el caso de todos modos si el objetivo function
's era bind(&A::foo, &a)
.
'std :: function' es una interfaz, no una implementación. Si desea preguntar sobre stdlib VC++ 's, libstdC++ o libC++ _specifically_ entonces eso es una pregunta válida, pero tal y como está tu pregunta es demasiado amplia – ildjarn
@ildjarn: Lea la última frase de la cuestión. Pregunta sobre implementaciones específicas, y más específicamente sobre libstdC++. – abarnert
@abarnert: ¿Cómo podría responder sin leerlo? Obviamente lo leí, y lo encuentro demasiado amplio. – ildjarn