Functores y clases con `operator()`
En C++, una clase puede comportarse como una función si sobrecarga el operador de llamada operator().
A estos objetos se les denomina functores (function objects).
Permiten encapsular lógica configurable dentro de un objeto invocable, combinando:
- la sintaxis de las funciones, y
- la organización y el estado interno de las clases.
Antes de las lambdas (introducidas en C++11), los functores eran la forma más habitual de representar acciones o estrategias intercambiables. Aun hoy siguen siendo útiles cuando se necesita comportamiento configurable con estado persistente.
Ejemplo básico: un objeto invocable
#include <iostream>
class Sumar {
public:
int operator()(int a, int b) const {
return a + b;
}
};
int main() {
Sumar sumar;
std::cout << "Resultado: " << sumar(3, 4) << '\n'; // 7
}
La clase Sumar define operator(), lo que permite invocar el objeto como si fuera una función:
sumar(3, 4) llama internamente a sumar.operator()(3, 4).
Functor con estado interno
Los functores pueden guardar información que modifique su comportamiento:
#include <iostream>
class Multiplicador {
private:
int factor_;
public:
explicit Multiplicador(int factor) : factor_(factor) {}
int operator()(int x) const {
return x * factor_;
}
};
int main() {
Multiplicador por2(2);
Multiplicador por10(10);
std::cout << por2(5) << '\n'; // 10
std::cout << por10(3) << '\n'; // 30
}
Cada instancia de Multiplicador representa un comportamiento distinto.
El estado factor_ personaliza la acción sin necesidad de escribir clases derivadas.
Inyección de comportamiento con functores
Los functores también pueden usarse para inyectar comportamiento configurable en otras clases o funciones,
igual que hicimos antes con lambdas o std::function.
#include <iostream>
#include <vector>
class Accion {
public:
virtual void operator()(int x) const = 0;
virtual ~Accion() = default;
};
class Mostrar : public Accion {
public:
void operator()(int x) const override {
std::cout << "Valor: " << x << '\n';
}
};
class Cuadrado : public Accion {
public:
void operator()(int x) const override {
std::cout << x << "² = " << x * x << '\n';
}
};
// El procesador no es dueño de la acción, solo la usa
class Procesador {
public:
explicit Procesador(const Accion& accion) : accion_(accion) {}
void ejecutar(const std::vector<int>& datos) const {
for (int valor : datos)
accion_(valor);
}
private:
const Accion& accion_; // referencia constante, sin propiedad
};
int main() {
std::vector<int> numeros = {1, 2, 3};
Mostrar mostrar;
Cuadrado cuadrado;
Procesador p1(mostrar);
Procesador p2(cuadrado);
p1.ejecutar(numeros);
p2.ejecutar(numeros);
}
- Cada clase derivada (
Mostrar,Cuadrado) define su propio comportamiento sobre el operador(), actuando como functor (objeto que se puede “llamar” como una función). - El
Procesadorrecibe una referencia a un objetoAccion, sin conocer su tipo concreto, lo que permite inyectar diferentes comportamientos en tiempo de ejecución. - La llamada
accion_(valor)dentro deProcesador::ejecutarinvoca polimórficamente el operador()del functor concreto, aplicando la acción específica a cada elemento del vector.