2012-06-29 15 views
7

Tengo esta clase C++ que un gran método complicado compute que me gustaría alimentar con un "kernel de cálculo", un método de la misma clase. Calculo que iba a hacer algo en la línea deCómo pasar mejor los métodos a los métodos de la misma clase

class test { 
int classVar_ = 42; 

int compute_add(int a, int b) 
{ 
    compute(int a, int b, this->add_()) 
} 

int compute_mult(int a, int b) 
{ 
    compute(int a, int b, this->mult_()) 
} 


int compute_(int a, int b, "pass in add or multiply as f()") 
{ 
    int c=0; 
    // Some complex loops { 
    c += f(a,b) 
    // } 
    return c; 
} 

int add_(int a, int b){a+b+classVar_;} 
int multiply_(int a, int b){a*b+classVar_;} 
... 

} 

pero no estoy seguro de cómo iba a pasar en add o multiply. Una alternativa a este enfoque sería pasar un ENUM de algún tipo para especificar add() o multiply(), pero quería evitar un switch o if dentro de los bucles.

¿Qué es mejor práctica aquí?

+1

Tiene una etiqueta 'member-function-pointers'. ¿No es ese un buen lugar para comenzar? – chris

+0

@chris supongo que está confundido acerca de la sintaxis? – RedX

+1

Si los métodos que desea aprobar son los mismos que en su muestra, las funciones simples (estáticas) y los punteros de funciones normales son suficientes. – Mat

Respuesta

5

Como sospechabas, pasar un puntero de función miembro es una práctica aceptable.

Si necesita conocer la sintaxis, que es:

int compute_(int a, int b, int (test::*f)(int,int)) 
{ 
    int c=0; 
    // Some complex loops { 
    c += (this->*f)(a,b) 
    // } 
    return c; 
} 

En representación de las funciones miembro utilizando números enteros, y conmutación, supone cargas programador para mantener las cosas al día cuando la lista de cambios de operaciones disponibles. Entonces no quieres eso a menos que haya alguna razón importante en un caso particular.

Una alternativa es hacer compute incluso más general - en lugar de tomar una función miembro, escribir una plantilla de función que toma cualquier tipo exigible:

template <typename BinaryFunction> 
int compute_(int a, int b, BinaryFunction f) { 
    // body as before but `f(a,b)` instead of `(this->*f)(a,b)` 
} 

Esta plantilla más general es grande si alguien quiere usar con un operador de su propia invención, que no es una función miembro de test. Sin embargo, es más difícil de usar en el caso de la función de miembro porque alguien necesita capturar this. Hay algunas formas de hacerlo: un C++ 11 lambda, boost::bind, o escribir un functor a mano. Por ejemplo:

template <typename BinaryFunction> 
int compute_(int a, int b, BinaryFunction f) { 
    // body as before with `f(a,b)` 
} 

int compute_(int a, int b, int (test::*f)(int,int)) 
{ 
    return compute_(a, b, bind_this(f, this)); 
} 

Definición bind_this es un poco de dolor: es como std::bind1st excepto que nos gustaría trabajar con un 3-arg funtor mientras que bind1st sólo toma un funtor binario. boost::bind y std::bind en C++ 11, son más flexibles y manejarán los argumentos adicionales.A continuación se va a hacer por este caso, pero no funciona en general para unir las funciones miembro 2-arg:

struct bind_this { 
    int (test::*f)(int,int); 
    test *t; 
    int operator(int a, int b) const { 
     return (t->*f)(a,b); 
    } 
    bind_this(int (test::*f)(int,int), test *t) : f(f), t(t) {} 
}; 

En C++ 11 sólo se puede utilizar una lambda:

int compute_(int a, int b, int (test::*f)(int,int)) 
{ 
    return compute_(a, b, [=](int c, int d){ return (this->*f)(c,d) }); 
} 
+0

¿Cómo llamarías 'compute_ (int a, int b, int (prueba :: * f) (int, int))'? 'compute_ (1,2, this-> add_)' produce 'no se puede inicializar un parámetro de tipo 'int (test :: *) (int, int)' con un valor r de tipo '''. 'compute_ (1,2, & this-> add_)' produce 'no se puede crear un puntero no constante a la función miembro'. –

+0

@Nico: intente simplemente 'compute_ (1,2, add_)'. – Mat

+0

@Mat Bueno, eso es lo mismo que 'this-> add_', el calificador adicional solo le dice al compilador dónde buscar' add_'. –

1

tiene dos alternativas:

  1. utilizando pointer to member function
  2. utilizando lambda functions

Ejemplo utilizando el puntero a la función miembro:

#include <iostream> 

class D 
{ 
public: 
    D(int v) : classVar_(v){} 
    int add_(int a, int b){return (a+b+classVar_);} 
    int multiply_(int a, int b){return (a*b+classVar_);} 
private: 
    int classVar_; 
}; 

class test { 
public: 

int compute_(int a, int b, D &d, int (D::*f)(int a, int b)) 
{ 
    int c=0; 
    // Some complex loops { 
    c += (d.*f)(a,b); 
    // } 
    return c; 
} 

}; 

int main() 
{ 
    test test; 
    D d(1); 

    std::cout<<"add : " << test.compute_(5, 4, d, &D::add_) << std::endl; 
    std::cout<<"add : " << test.compute_(5, 4, d, &D::multiply_) << std::endl; 
} 

Ejemplo usando lambda:

#include <iostream> 
#include <functional> 

class D 
{ 
public: 
    D(int v) : classVar_(v){} 
    int add_(int a, int b){return (a+b+classVar_);} 
    int multiply_(int a, int b){return (a*b+classVar_);} 
private: 
    int classVar_; 
}; 

class test { 
public: 

int compute_(int a, int b, std::function< int(int,int) > f) 
{ 
    int c=0; 
    // Some complex loops { 
    c += f(a,b); 
    // } 
    return c; 
} 

}; 

int main() 
{ 
    test test; 
    D d(1); 

    std::cout<<"add : " << test.compute_(5, 4, [&d](int a, int b){ return d.add_(a,b); }) << std::endl; 
    std::cout<<"add : " << test.compute_(5, 4, [&d](int a, int b){ return d.multiply_(a,b); }) << std::endl; 
} 
+0

¿Es posible fusionar las clases 'test' y' D'? –

+0

@Nico Sí, puedes fusionarlos. Los dividí porque hace que el ejemplo sea más general. –

1

Uso punteros a las funciones.

int compute(int a, int b, int (test::*f) (int, int)) 
{ 
    int c=0; 
    // Some complex loops { 
    c += (this->*f)(a,b) 
    // } 
    return c; 
} 
Cuestiones relacionadas