Disponible la nueva versión "donationware" 7.3 de OrganiZATOR
Descubre un nuevo concepto en el manejo de la información.
La mejor ayuda para sobrevivir en la moderna jungla de datos la tienes aquí.

Curso C++

[Home]  [Inicio]  [Índice]


5.1.3a1  Objetos-Función

§1  Recordatorio

Un objeto-función (o función-objeto) es una instancia de cierta clase en la que se ha sobrecargado el operador de invocación de función ( ) ( 4.9.16) definiendo la función-operador operator( ) como función-miembro.

Nota:  ver en ( 4.9.18f) una completa justificación e introducción a este concepto.

Cuando un objeto-función es utilizado como si fuese una función (con la sintaxis tradicional de invocación de funciones), en realidad se invoca la función operator( ) sobre esa instancia de la clase.

Considere la definición siguiente:

class mayorQueTres {
  public:
  bool operator() (int val) { return val > 3; }
};

...

 

void func() {

  mayorQueTres m;     // m es un objeto-función

  in x;

  ...

  if ( m(x) ) cout << "ahora x es mayor que 3" << endl;

  ...

}

En este ejemplo, m(x) equivale en realidad a la invocación:  m.operator(x), y esta función devuelve un bool.  El punto interesante es que estamos utilizando un objeto m como si fuese una función (con su sintaxis), aunque en realidad estamos invocando uno de sus métodos.


Para generalizar esta clase definimos una nueva versión, MayorQue añadiéndole un constructor y una propiedad valorReferencia, que es una constante entera, cuyo valor es iniciado por el constructor mediante el adecuado inicializador ( 4.11.2d3).

class MayorQue {
  public:
  const int valorReferencia;
  MayorQue (int x) : ValorReferencia(x) { }    // constructor
  bool operator() (int val) { return val > valorReferencia; }
};


El resultado es que tenemos un objeto-función general MayorQueX,  donde el valor de X viene determinado cuando instanciamos la clase.  Este objeto-función, que devuelve un bool, puede utilizarse como argumento en aquellos algoritmos de la STL que requieren un predicado ( 5.1.3a).

Como curiosidad, señalaremos que la instanciación del objeto puede realizarse en el mismo momento de utilizarlo como argumento. Por ejemplo, el código que sigue busca el primer valor mayor que 12 de una lista:

list<int>::iterator primerMayor =
    find_if (aList.begin(), aList.end(), MayorQue(12));


Observe que el último argumento  MayorQue(12), supone en realidad pasar a la función genérica find_if() un objeto e de tipo MayorQue en el que e.valorReferencia == 12.  Insistimos en que no se trata del resultado de una invocación a función;  al menos no de una función normal, sino del constructor de la clase, que devuelve un objeto-función.  En el contexto de find_if(....),   MayorQue(12), es un puntero-a-método de un objeto temporal.

§2  Uso de objetos-función

Muchos objetos-función, como las instancias de la clase MayorQueX anteriormente descrita, contienen funciones operator() que devuelven un bool.  Son predicados ( 5.1.3a) que aceptan a su vez uno o dos argumentos, por lo que pueden ser adecuadas como argumentos de aquellos algoritmos de la STL que requieren un Predicate o BinaryPredicate.

Se presentan mucha situaciones en las que es conveniente sustituir funciones por objetos-función. En muchos casos, la utilización de una función-objeto para invocar un método de una instancia, puede ser objeto de una sustitución inline ( 4.4.6b), lo que permite reducir la sobrecarga inherente al mecanismo de invocación de funciones.

Nota: siempre es preferible utilizar un objeto-función de la Librería Estándar que escribir una función nueva.  En este sentido es interesante conocer las opciones ofrecidas por las Librerías C++ en general, y la STL en particular, para no tener que reinventar constantemente la rueda.  Además de estar muy pensados y probados, estos algoritmos están muy optimizados, lo que resulta en un código muy rápido; fiable; compacto, y fácil de manejar.


El propio Estándar (ISO/IEC 14882. §20.3) declara que los objetos-función son importantes para la utilización eficiente de la Librería; que en aquellas situaciones en que se precisa pasar un puntero-a-función como argumento de una función genérica, se ha recurrido a utilizar un objeto en el que se ha definido un método operator(). Y que este recurso, además de permitir a los algoritmos-plantilla funcionar con punteros-a-función, les permite trabajar con cualquier objeto-función definido por el usuario, lo que los hace más genéricos.  Abundando en esta idea, incluimos un párrafo de Bill Kempf, autor de la versión original de la librería Boost.threads, en su artículo "The Boost.Threads Library".

"However, because Boost.Threads uses a function object instead of just a function pointer, it is possible for the function object to carry data needed by the thread. This approach is actually more flexible and is type safe. When combined with functional libraries, such as Boost.Bind, this design actually allows you to easily pass any amount of data to the newly created thread".

Estos asuntos serán tratados en las próximas secciones, pero adelantamos aquí que en la Librería Estándar C++ existe un amplio conjunto de objetos-función disponibles para su uso [1].

§3  Objetos-función incluidos en la Librería Estándar
Objeto-función Operación implementada
Operaciones aritméticas ( 4.9.1)
plus suma  x + y
minus resta  x - y
multiplies multiplicacióon  x * y
divides división  x / y
modulus resto o módulo  x % y
negate negación  - x
Operaciones de comparación  ( 4.9.12)
equal_to verificación de identidad  x == y
not_equal_to verificación de desigualdad  x != y
greater comparación mayor-que  x > y
less comparación menor-que  x < y
greater_equal comparación mayor o igual-que  x >= y
less_equal comparación menor o igual-que  x <= y
Operaciones lógicas  ( 4.9.8)
logical_and conjunción lógica (AND)  x && y
logical_or disyunción lógica (OR)  x || y
logical_not negación lógica (NOT)  ! x


Estos objetos-función se han implementado a partir de dos clases genéricas definidas en el fichero <functional> de la STL de nominadas unary_function y binary_function (en realidad son estructuras -todos sus miembros son públicos-):

template <class Arg, class Result> struct unary_function;
template <class Arg1, class Arg2, class Result> struct binary_function;


  Inicio.


[1]  En realidad estas entidades se introdujeron porque eran necesarias para las sofisticadas técnicas de programación que se aplicaron en la Librería Estándar.  En concreto, funciones que reciben funciones como argumentos.