En C++ le llamamos functors a objectos que pueden actuar como funciones. Esto se logra por medio de la sobrecarga al operator(), que permite cualquier cantidad de parametros. Ahora, ¿para qué usamos functors? Son utiles mas que todo cuando se ocupa una funcion que guarde estado. Por ejemplo, una funcion que revise si sus dos parametros son iguales.
bool equal(int x, int y) { return x == y; }
A esta funcion siempre hay que pasarle dos parametros. Es una funcion bien sencilla, y no es de mucha utilidad ya que hace lo mismo que el operator==. Ahora, ¿qué tal si queremos llamar a la funcion varias veces con el mismo parametro para x, pero cambiar y? Usamos un functor para guardar el valor de x.
struct equal { int x; equal(int x) : x(x) {} bool operator()(int y) const { return x == y; } }; int main() { equal e(10); e(10); // 10 == 10 e(2); // 10 == 2 e(-10); // 10 == -10 }
La estructura equal es un functor. La podrias usar para pasarla a los algoritmos de la libreria estandar. Por ejemplo, para buscar un elemento de cierto valor en un vector.
Nota: Algunos algoritmos de la libreria estandar tienen requerimientos para los functors. Para mas informacion, lean sobre std::unary_function y std::binary_function.
int main() { std::vector<int> v(10, 15); find_if(v.begin(), v.end(), equal<int>(10)); }
Si ya saben un poco de la libreria estandar, van a saber que ya existe una funcion std::find, que hace esto sin necesidad de un functor. Lo util de hacerlo en un functor es que se puede encadenar con otros functors. Por ejemplo, se puede hacer un functor not_, de modo que not_(equal<int>(10)) sea igual que usar el operator!=. De hecho, ya existen std::equal y std::not1. Ahora, para hacerlo bien y con templates:
template <typename T> struct equal_t { T x; equal_t(T const& x) : x(x) {} template <typename Y> bool operator()(Y const& y) const { return x == y; } }; // Esto nos permite hacer equal(valor) en vez de equal_t<tipo>(valor) template <typename T> equal_t<T> equal(T const& x) { return equal_t<T>(x); } // Sobrecarga para que funcione equal("hola") template <typename T, int N> equal_t<T const*> equal(T const (&x)[N]) { return equal_t<T const*>(x); } int main() { std::vector<std::string> v(10, "hola"); find_if(v.begin(), v.end(), equal("mundo")); }
Esa clase soporta cualquier tipo que tenga sobrecargado el operator==. Esto se puede extender mucho mas que lo que hemos visto aqui. Más adelante explicaré como se implementan functors más complicados, como los que se ven en Boost.Bind.