2009-06-22 15 views
10

¿Cuál de estos 2 métodos es mejor y por qué?Usando algoritmos STL, ¿es mejor pasar un puntero de función o un functor?

Método 1:

void fun(int i) { 
    //do stuff 
} 

... 
for_each(a.begin(), a.end(), fun); 

Método 2:

class functor { 
public: 
    void operator()(int i); 
}; 

... 
for_each(a.begin(), a.end(), functor()); 

Editar: Debería haber formulado de esta manera, en qué situación es uno del método anterior preferible a la otra?

¡Muchas gracias!

Respuesta

19

Functors pueden (y lo hará ) ser inline trivialmente - esto no está hecho para los punteros de función regulares.

Por lo tanto, los funtores tienen un beneficio de rendimiento real que puede ser enorme en bucles apretados. Además, los funtores son generalmente más fáciles de componer y en particular juegan mejor con el STL: std::bindx no funciona en los punteros de función, por ejemplo.

Odio cómo confunden el código, pero dadas todas las ventajas, preferiría que sobre punteros a la función en cualquier momento.

+0

¿En qué sentido juegan mejor con los algoritmos STL? –

+1

Ver mi actualización. Básicamente, su enlace es el problema aquí. –

+0

No std :: ptr_fun se ocupa de cómo usar un puntero de función con std :: bindXXX? – MSN

6

Mi opinión - # 1 es mejor, porque es más simple.

El hecho de que algo pueda ser un objeto, no significa que deba serlo. Estoy seguro de que hay casos en los que el functor tiene sentido, pero en la mayoría de los casos probablemente no sea necesario.

1

Un functor puede ser more easily inlined, por lo que puede ser un factor a considerar cuando el rendimiento es importante.

+1

No existe un puntero a la función real _variable_ definido aquí, por lo que desde una vista entrante, diría que son los mismos. –

+2

No, no son lo mismo desde un punto de vista interno. Piensa en los tipos con los que se ejemplifica con_each. En el caso del puntero de función, se crea una instancia con un argumento de tipo void (*) (int). En el segundo caso, con un argumento de tipo functor. El primero no dice nada acerca de qué función se llama, por lo que no se puede insertar fácilmente. Esto último significa que el compilador puede deducir trivialmente que el functino para llamar es functor :: operator()() – jalf

+1

Tomar un puntero a una función evita que el compilador lo delimite. Siempre. –

7

Una gran ventaja de un objeto de función sobre un puntero de función es que puede enlazar más fácilmente algunos argumentos en la construcción del objeto de función.

Un ejemplo de un funtor que podrían hacer esto sería

class multiplyBy 
    { 
    private: 
     int m_whatToMultiplyBy; 
    public: 
     multiplyBy(int whatToMultiplyBy) : 
      m_whatToMultiplyBy(whatToMultiplyBy) 
     { 
     } 

     void operator()(int& i) 
     { 
      i = m_whatToMultiplyBy * i; 
     } 
    } 


    ... 

    // double the array 
    for_each(a.begin(), a.end(), multiplyBy(2)); 

Esta "unión" de los argumentos se puede hacer muy bien con boost::bind y boost::function si el refuerzo está disponible para usted.

1

# 1 es más fácil declarar la función
mientras que # 2 el functor se parece más a una llamada de función.

(En algún momento usted tiene a la desesperación de la sintaxis ++ c)

11

Para aclarar una idea errónea de lo que puede compilar un compilador, un compilador suficientemente bueno puede insertar punteros de función. Puede alinear los objetos de función más fácilmente ya que hay más información estática disponible. Por ejemplo, un puntero a una función que no toma parámetros y devuelve un bool es de tipo bool (*)(), mientras que un funtor tiene un tipo explícito, es decir, el funtor, y la instanciación de la plantilla puede llamar estáticamente al operador del functor, que tener que llamar a través de un puntero de función.

En la práctica, sin embargo, es principalmente una cuestión de dar al compilador información suficiente para optimizar con eficacia.

Por ejemplo, Visual C++ 2008, da el siguiente código con optimizaciones completos:

#include "stdafx.h" 
#include <algorithm> 

const char print_me[]= "hello!"; 

class print_functor 
{ 
public: 
    void operator()(char c) 
    { 
     printf("%c", c); 
    } 
}; 

void print_function(char c) 
{ 
    printf("%c", c); 
} 

int _tmain(int argc, _TCHAR* argv[]) 
{ 
    std::for_each(print_me, print_me + sizeof(print_me)/sizeof(print_me[0]), print_functor()); 
    printf("\n"); 

    std::for_each(print_me, print_me + sizeof(print_me)/sizeof(print_me[0]), print_function); 

    return 0; 
} 

inlines ambos std::for_each llamadas por completo. Por cierto, en la PC, el primer for_each tiene un lea ecx, [ecx] innecesario.

+0

¡Muy perspicaz gracias! –

Cuestiones relacionadas