c++ - smart - ¿Cómo puede ser útil sobrecargar al operador de "llamada de función"?
smart contracts ethereum (7)
A un algoritmo implementado usando una plantilla no le importa si lo que se llama es una función o un functor, le importa la sintaxis. Ya sea los estándares (por ejemplo, for_each ()) o los tuyos. Y los funtores pueden tener estado, y hacer todo tipo de cosas cuando se les llama. Las funciones solo pueden tener estado con una variable local estática o variables globales.
Recientemente descubrí que en C ++ puedes sobrecargar el operador de "llamada de función", de una manera extraña en la que tienes que escribir dos pares de paréntesis para hacerlo:
class A {
int n;
public:
void operator ()() const;
};
Y luego usarlo de esta manera:
A a;
a();
¿Cuándo es esto útil?
El compilador también puede en línea el functor y la llamada de función. Sin embargo, no puede insertar un puntero de función. De esta manera, el uso del operador de llamada de función puede mejorar significativamente el rendimiento cuando se usa, por ejemplo, con los algoritmos estándar de bibliotecas de C ++.
Esto se puede usar para crear en.wikipedia.org/wiki/Function_object , objetos que actúan como funciones:
class Multiplier {
public:
Multiplier(int m): multiplier(m) {}
int operator()(int x) { return multiplier * x; }
private:
int multiplier;
};
Multiplier m(5);
cout << m(4) << endl;
Las impresiones de arriba 20
. El artículo de Wikipedia vinculado anteriormente da ejemplos más sustanciales.
Hay poco más que una ganancia sintáctica en el uso de operator () hasta que comienzas a usar plantillas. Pero cuando usa plantillas, puede tratar funciones reales y funtores (clases que actúan como funciones) de la misma manera.
class scaled_sine
{
explicit scaled_sine( float _m ) : m(_m) {}
float operator()(float x) const { return sin(m*x); }
float m;
};
template<typename T>
float evaluate_at( float x, const T& fn )
{
return fn(x);
}
evaluate_at( 1.0, cos );
evaluate_at( 1.0, scaled_sine(3.0) );
Por ejemplo para la implementación de generadores:
// generator
struct Generator {
int c = 0;
virtual int operator()() {
return c++;
}
};
int sum(int n) {
Generator g;
int res = 0;
for( int i = 0; i < n; i++ ) {
res += g();
}
return res;
}
Si está creando una clase que encapsula un puntero de función, esto podría hacer que el uso sea más obvio.
Veo potencial para un solo uso exótico:
Supongamos que tiene un objeto de tipo desconocido y tiene que declarar otra variable del mismo tipo, como esta:
auto c=decltype(a*b)(123);
Cuando dicho patrón se usa extensivamente, el tipo de letra se vuelve muy molesto. Este caso puede ocurrir cuando se usa algún sistema de tipo inteligente que inventa automáticamente el tipo de resultado de las funciones y los operadores en función de los tipos de argumentos.
Ahora, si cada especialización de cada tipo de ese tipo de sistema equipado con una definición mágica de operator()
así:
template<????> class Num<???>{
//specific implementation here
constexpr auto operator()(auto...p){return Num(p...);}
}
decltype()
ya no es necesario, puede escribir simplemente:
auto c=(a*b)(123);
Porque el operador () del objeto redirige al constructor de su propio tipo.