2012-01-29 7 views

Respuesta

34

Un puntero a la función es la dirección de una función real definida en C++. Un std::function es un contenedor que puede contener cualquier tipo de objeto invocable (objetos que se pueden usar como funciones).

struct FooFunctor 
{ 
    void operator()(int i) { 
     std::cout << i; 
    } 
}; 

// Since `FooFunctor` defines `operator()`, it can be used as a function 
FooFunctor func; 
std::function<void (int)> f(func); 

Aquí, std::function que permite abstraer exactamente qué tipo de objeto exigible es que se trata de — usted no sabe que es FooFunctor, que acaba de saber que devuelve void y tiene una int parámetro.

Un ejemplo del mundo real donde esta abstracción es útil es cuando usa C++ junto con otro lenguaje de scripting. Es posible que desee diseñar una interfaz que pueda tratar las funciones definidas en C++, así como las funciones definidas en el lenguaje de scripting, de una manera genérica.

Editar:Encuadernación

Junto std::function, también puede encontrar std::bind. Estas dos herramientas son muy poderosas cuando se usan juntas.

void func(int a, int b) { 
    // Do something important 
} 

// Consider the case when you want one of the parameters of `func` to be fixed 
// You can used `std::bind` to set a fixed value for a parameter; `bind` will 
// return a function-like object that you can place inside of `std::function`. 

std::function<void (int)> f = std::bind(func, _1, 5); 

En ese ejemplo, el objeto de función devuelto por bind toma el primer parámetro, _1, y lo pasa a func como el parámetro a, y establece b ser la constante de 5.

+3

¿qué es _1 aquí? – makar

+1

@makar Es el identificador utilizado por 'std :: bind' para referirse al primer argumento de la función resultante. Es un poco como: 'void bound (int _1) {func (_1, 5); } ' –

+0

[El enlace aquí] (http://stackoverflow.com/questions/25848690/should-i-use-stdfunction-or-a-function-pointer-in-c) menciona otro diferente entre el puntero de función y la función std :: : La captura lambda no se puede utilizar en el puntero de función. Así que si intenta usar lambda '[&] (int a, int b) {/ * blah * /}', o '[=] (int a, int b) {/ * blah * /} ', tendrá un error de compilación. El único formato válido es '[] (int a, int b) {/ * blah * /}' – r0ng

4

No.

Uno es un puntero de función; el otro es un objeto que sirve como envoltorio alrededor de un puntero de función.

Ellos prácticamente representan lo mismo, pero es std::function ahora más potente, que le permite hacer que las consolidaciones y otras cosas.

+0

¿Hay una manera simple de convertir entre estos dos? – phimuemue

+0

@phimuemue: Eso no tiene sentido en el caso general. –

+0

@phimuemue Usted escribe una función de envoltura que toma una función std :: como puntero de datos, y luego llama a la función std ::. El puntero de función debe tener un puntero de datos para que esto funcione. – Lalaland

11

A std::function tiene estado. Puede contener parámetros adicionales "vinculados" en él.

Estos parámetros pueden variar desde cosas como otras clases, otras funciones, o incluso este punteros para llamadas a funciones de miembros.

El puntero de función de reemplazo no typedef int (*fn)(int);

es Es typedef int (*fn)(void*,int);, con el void* reperensting el estado que se oculta en el std::function.

40

Ellos no son los mismos en todos. std::function es un tipo complejo, pesado, con estado, casi mágico que puede contener cualquier tipo de entidad invocable, mientras que un puntero a la función es solo un simple puntero. Si usted puede salirse con la suya, se debe preferir cualquiera de los punteros de función desnudas o auto - tipos bind/auto -lambda. Solo use std::function si realmente necesita una forma sistemática de organizar una colección heterogénea de entidades invocables, como funciones, funtores, captura de lambdas y expresiones de enlace.


Actualizar: un poco de explicación sobre auto tipos: Comparación de las dos funciones siguientes:

void do_something_1(std::function<void(int)> f, int a) { f(a); } 

template <typename F, typename A> void do_something_2(F f, A a) { f(a); } 

Ahora imagine la invocación de ellos con una lambda o una expresión bind:

do_something_X([foo, &bar](int n){ bar += n*foo; },  12); 
do_something_X(std::bind(X::bob, &jim, true, _1, Blue), 13); 

La segunda versión con la plantilla es más eficiente, porque en ambos casos, el argumento F se deduce al tipo real e incognoscible de la expresión. La primera versión, con std::function, no es una plantilla y puede parecer más simple y deliberada, pero siempre fuerza la construcción del objeto std::function, y muy posiblemente conlleva varios borrados de tipo y costos de despacho virtual.

+0

Esto es un poco confuso, ¿no es así? es decir, exactamente cuándo usar std :: function, y cuándo usar un puntero de función. Además, ¿qué es un "auto-bind/auto-lambda types" – aCuria

+4

@aCuria: si quieres hacer un contenedor de cosas invocables y quieres poner todo tipo de tipos mixtos allí, entonces * tienes * que usar ' std :: function' y convierte todo. Pero si sabes que siempre tienes funciones gratuitas genuinas, puedes crear un contenedor de indicadores de función. Expandiré la preferencia automática en la publicación actual en un momento. –

+1

@aCuria: ¡Actualizada! –

Cuestiones relacionadas