overload operator c++ boost operator-overloading functor function-call-operator

overload c++ operator



¿Por qué anular el operador()? (11)

Comience a usar std::for_each , std::find_if , etc. con más frecuencia en su código y verá por qué es útil tener la capacidad de sobrecargar al operador (). También permite que los funtores y las tareas tengan un método de llamada claro que no entre en conflicto con los nombres de otros métodos en las clases derivadas.

En la biblioteca de Boost Signals , están sobrecargando el operador ().

¿Es esta una convención en C ++? Para devoluciones de llamada, etc.?

Lo he visto en código de un compañero de trabajo (que es un gran fanático de Boost). De toda la bondad de Boost, esto solo me ha llevado a la confusión.

¿Alguna idea del motivo de esta sobrecarga?



Los funtores son básicamente como indicadores de función. En general, están destinados a ser copiables (como punteros de función) e invocados de la misma manera que los punteros de función. El principal beneficio es que cuando tienes un algoritmo que funciona con un functor con plantilla, la llamada de función a operator () puede estar en línea. Sin embargo, los punteros de función siguen siendo funtores válidos.


Muchos han respondido que se trata de un funtor, sin decir una razón importante por la cual un functor es mejor que una simple función antigua.

La respuesta es que un funtor puede tener estado. Considere una función de suma: necesita mantener un total acumulado.

class Sum { public: Sum() : m_total(0) { } void operator()(int value) { m_total += value; } int m_total; };


Otras publicaciones han hecho un buen trabajo al describir cómo funciona el operador () y por qué puede ser útil.

Recientemente he estado usando un código que hace un uso muy extenso de operator (). Una desventaja de sobrecargar este operador es que algunos IDEs se vuelven herramientas menos efectivas como resultado. En Visual Studio, por lo general, puede hacer clic con el botón derecho en una llamada a método para ir a la definición y / o declaración del método. Desafortunadamente, VS no es lo suficientemente inteligente como para indexar llamadas de operador (). Especialmente en código complejo con definiciones de operador () reemplazadas por todas partes, puede ser muy difícil determinar qué parte del código se está ejecutando. En varios casos, descubrí que tenía que ejecutar el código y rastrearlo para encontrar lo que realmente se estaba ejecutando.


Otro compañero de trabajo señaló que podría ser una forma de disfrazar los objetos del functor como funciones. Por ejemplo, esto:

my_functor();

Realmente es:

my_functor.operator()();

Entonces eso quiere decir esto:

my_functor(int n, float f){ ... };

¿Se puede usar para sobrecargar esto también?

my_functor.operator()(int n, float f){ ... };


Permite que una clase actúe como una función. Lo he usado en una clase de registro donde la llamada debería ser una función, pero quería el beneficio extra de la clase.

algo así:

logger.log("Log this message");

se convierte en esto:

logger("Log this message");


También puede consultar el ejemplo de matriz de C ++ faq . Hay buenos usos para hacerlo, pero por supuesto depende de lo que estás tratando de lograr.


Un functor no es una función, por lo que no puede sobrecargarlo.
Sin embargo, su compañero de trabajo está en lo cierto al decir que la sobrecarga del operador () se usa para crear "funtores", objetos que pueden llamarse funciones similares. En combinación con plantillas que esperan argumentos "funcionales", esto puede ser bastante poderoso porque la distinción entre un objeto y una función se vuelve borrosa.

Como han dicho otros carteles: los funtores tienen una ventaja sobre las funciones simples en cuanto a que pueden tener estado. Este estado se puede usar en una única iteración (por ejemplo, para calcular la suma de todos los elementos en un contenedor) o en múltiples iteraciones (por ejemplo, para encontrar todos los elementos en múltiples contenedores que satisfagan determinados criterios).


Una de las fortalezas que puedo ver, sin embargo, esto se puede discutir, es que la firma del operador () se ve y se comporta de la misma manera en diferentes tipos. Si tuviéramos un reportero de clase que tenía un informe de método de miembro (...), y luego otro escritor de clase, que tenía un método de escritura de miembro (...), tendríamos que escribir adaptadores si quisiéramos usar ambas clases como quizás un componente de plantilla de algún otro sistema. Todo lo que importa es pasar cuerdas o lo que sea. Sin el uso de la sobrecarga del operador () o la escritura de adaptadores de tipo especial, no podría hacer cosas como

T t; t.write("Hello world");

porque T tiene el requisito de que haya una función miembro llamada write que acepte cualquier elemento implícitamente moldeable para const char * (o más bien const char []). La clase Reporter en este ejemplo no tiene eso, por lo que tener T (un parámetro de plantilla) como Reporter no podría compilarse.

Sin embargo, hasta donde puedo ver, esto funcionaría con diferentes tipos

T t; t("Hello world");

sin embargo, todavía requiere explícitamente que el tipo T tenga dicho operador definido, por lo que todavía tenemos un requisito sobre T. Personalmente, no creo que sea demasiado extraño con los funtores ya que se usan comúnmente, pero preferiría ver otros mecanismos para este comportamiento. En idiomas como C #, podría pasar un delegado. No estoy muy familiarizado con los punteros de función de miembro en C ++, pero podría imaginar que también podría lograr el mismo comportamiento allí.

Además del comportamiento del azúcar sintético, realmente no veo las fortalezas de la sobrecarga del operador para realizar tales tareas.

Estoy seguro de que hay más personas a sabiendas que tienen mejores razones que yo, pero pensé que podría expresar mi opinión para que el resto de ustedes la compartan.


Uno de los objetivos principales al sobrecargar al operador () es crear un functor. Un functor actúa como una función, pero tiene las ventajas de que es estable, lo que significa que puede mantener los datos reflejando su estado entre llamadas.

Aquí hay un ejemplo de functor simple:

struct Accumulator { int counter = 0; int operator()(int i) { return counter += i; } } ... Accumulator acc; cout << acc(10) << endl; //prints "10" cout << acc(20) << endl; //prints "30"

Los funtores se utilizan mucho con la programación genérica. Muchos algoritmos STL están escritos de una manera muy general, de modo que usted puede conectar su propia función / functor en el algoritmo. Por ejemplo, el algoritmo std :: for_each le permite aplicar una operación en cada elemento de un rango. Podría implementarse algo así:

template <typename InputIterator, typename Functor> void for_each(InputIterator first, InputIterator last, Functor f) { while (first != last) f(*first++); }

Usted ve que este algoritmo es muy genérico ya que está parametrizado por una función. Al usar el operador (), esta función le permite usar un functor o un puntero de función. Aquí hay un ejemplo que muestra ambas posibilidades:

void print(int i) { std::cout << i << std::endl; } ... std::vector<int> vec; // Fill vec // Using a functor Accumulator acc; std::for_each(vec.begin(), vec.end(), acc); // acc.counter contains the sum of all elements of the vector // Using a function pointer std::for_each(vec.begin(), vec.end(), print); // prints all elements

En cuanto a su pregunta sobre la sobrecarga del operador (), bueno, sí es posible. Puede escribir perfectamente un functor que tenga varios operadores de paréntesis, siempre que respete las reglas básicas de sobrecarga de métodos (p. Ej., No es posible sobrecargar el tipo de devolución).