Functional es un archivo de encabezado en la biblioteca estándar del lenguaje de programación C++ que proporciona un conjunto de plantillas de clase para trabajar con objetos funcionales , así como un conjunto de clases auxiliares para su uso en algoritmos de biblioteca estándar .
El archivo de encabezado <funcional> apareció por primera vez en el lenguaje estándar en 1998 [1] , donde se agregó junto con la biblioteca de plantillas estándar. Inicialmente, incluía un conjunto de objetos funcionales auxiliares para la conveniencia de usar algoritmos STL . También incluía binders y un conjunto de envoltorios de funciones, cuyo objetivo era facilitar el trabajo en aquellos casos en los que se usaba activamente la transferencia de punteros a funciones, es decir, trabajar con funciones como con ciertos objetos. [2] Se propuso una adición significativa al archivo de encabezado en la biblioteca de extensión C++ TR1 [3] . Clases como function , bind , mem_fn , result_of , reference_wrapper , hash se transfirieron de la biblioteca Boost a STL . La mayoría de estos cambios, con la excepción de result_of , están incluidos en el estándar de lenguaje C++17 actual [4] . Dado que las clases de función y enlace duplican en gran medida la funcionalidad de los enlazadores y contenedores de función en la edición de 1998 del estándar, en C++ 11 estos últimos se designaron como obsoletos (obsoletos).
El documento estándar del lenguaje C++11 introduce los siguientes términos en relación con las clases de archivo de encabezado <funcional> .
Un objeto función, o funtor, es una clase con una función definida llamada operador - operator() de tal forma que en el siguiente código
FunctionObjectType func ; función ();la expresión func() es una llamada al operador() del objeto función func , no una llamada a alguna función llamada func . El tipo del objeto funcional debe definirse de la siguiente manera:
clase FunciónObjetoTipo { público : operador nulo () () { // Haz algo de trabajo } };El uso de objetos de función tiene una serie de ventajas [5] sobre el uso de funciones, a saber:
Los objetos de función que devuelven un tipo booleano se denominan predicados . La biblioteca estándar utiliza predicados binarios y unarios. El comportamiento de un predicado no debe depender del número de operaciones de copia realizadas en ese predicado, porque el estándar de C++ no especifica cuántas veces se puede copiar un predicado cuando se usa en algoritmos. En otras palabras, para que STL acepte un predicado personalizado, no debe cambiar su estado cuando se copia o se invoca.
Comenzando con el estándar C++11 , la clase de plantilla std::function es un envoltorio de función polimórfica para uso general. Los objetos de la clase std::function pueden almacenar, copiar y llamar a objetos invocables arbitrarios : funciones, expresiones lambda, expresiones vinculantes y otros objetos funcionales. En términos generales, en cualquier lugar donde sea necesario usar un puntero de función para llamarlo diferido, o para crear una función de devolución de llamada , se puede usar std::function en su lugar , lo que le da al usuario más flexibilidad en la implementación.
Esta clase apareció por primera vez en la biblioteca de funciones en Boost versión 1.23.0 [7] . Después de su desarrollo posterior, se incluyó en el estándar de extensión TR1 de C++ y se finalizó en C++11.
Definición de clase plantilla < clase > función de clase ; // plantilla indefinida < clase R , clase ... ArgTypes > función de clase < R ( ArgTypes ...) > ;El estándar también define modificadores auxiliares de intercambio y asignación y operadores de comparación ( == y != ) con nullptr . La función de destino accede al objeto de destino y target_type accede a su tipo . La función de operador de conversión booleana devuelve verdadero cuando la clase tiene un objeto de destino.
Ejemplo de uso #incluir <iostream> #incluir <funcional> estructura A { A ( int num ) : num_ ( num ){} void printNumberLetter ( char c ) const { std :: cout << "Número: " << num_ << " Letter: " << c << std :: endl ;} int num_ ; }; void printLetter ( char c ) { std :: cout << c << std :: endl ; } estructura B { operador vacío () () { std :: cout << "B()" << std :: endl ;} }; int principal () { // Contiene una función. std :: function < void ( char ) > f_print_Letter = printLetter ; f_print_Letter ( 'Q' ); // Contiene una expresión lambda. std :: function < void () > f_print_Hello = [] () { std :: cout << "¡Hola mundo!" << std :: endl ;}; f_print_hola (); // Contiene aglutinante. std :: function < void () > f_print_Z = std :: bind ( printLetter , 'Z' ); f_imprimir_Z (); // Contiene una llamada al método de clase. std :: function < void ( const A & , char ) > f_printA = & A :: printNumberLetter ; Aa ( 10 ) ; f_printA ( a , 'A' ); // Contiene un objeto de función. sib ; _ estándar :: función < vacío () > f_B = b ; f_b (); }El resultado del código anterior será:
q hola mundo ! Z Número : 10 Letra : A segundo () std::bad_function_callSe lanzará una excepción de tipo bad_function_call cuando se intente llamar a un envoltorio de función function::operator() si el envoltorio no tiene un objeto de destino. bad_function_call hereda de std::exception y tiene un método virtual what() disponible para obtener el texto del error. Ejemplo de uso:
#incluir <iostream> #incluir <funcional> int principal () { std :: function < void () > func = nullptr ; prueba { función (); } catch ( const std :: bad_function_call & e ) { std :: cout << e . qué () << std :: endl ; } }La función de plantilla std::mem_fn crea un objeto contenedor alrededor de los punteros a los miembros de la clase. Este objeto puede almacenar, copiar y llamar a un miembro de la clase mediante un puntero. Las referencias y los punteros inteligentes [8] también se pueden utilizar como puntero .
La función de plantilla std::mem_fn apareció por primera vez en la biblioteca de funciones miembro en Boost 1.25.0 [7] . También se incluyó en C++ TR1 y finalmente en C++11. En la biblioteca Boost, se desarrolló como una generalización de las funciones estándar std::mem_fun y std::mem_fun_ref .
Antes de la inclusión de partes de la biblioteca Boost en C++ 11, la biblioteca estándar tenía sus propias contrapartes contenedoras de funciones. Para ayudarlo a escribir objetos de función, la biblioteca proporciona las siguientes clases base.
plantilla < clase Arg , clase Resultado > estructura función_unaria { typedef Arg argumento_tipo ; typedef Resultado result_type ; }; plantilla < clase Arg1 , clase Arg2 , clase Resultado > estructura función_binaria { typedef Arg1 primer_argumento_tipo ; typedef Arg2 segundo_argumento_tipo ; typedef Resultado result_type ; };El propósito de estas clases es dar nombres estándar a los tipos de argumentos y devolver valores para evitar confusiones en el uso futuro de predicados personalizados. Los predicados personalizados, a su vez, le permiten usar contenedores y algoritmos STL de una manera simple y elegante, en particular, los predicados personalizados son útiles cuando necesita usar algoritmos para clases que no están desarrolladas en base a la biblioteca estándar [6] .
Sin embargo, el protocolo funcional basado en la herencia adaptativa que introdujeron estas clases fue reemplazado por funciones lambda y std::bind en C++11 [9] , y se volvió costoso mantener este protocolo para los nuevos componentes de la biblioteca. Además, deshacerse de la herencia resolvió algunas ambigüedades [10] . Por lo tanto, se decidió marcar estas clases como obsoletas en C++11 [4] .
El estándar tiene adaptadores de puntero de función y adaptadores de método de clase que están en desuso en el estándar C++ 11 porque duplican la funcionalidad de lo nuevo.
std::ptr_fun le permite crear contenedores alrededor de funciones de uno y dos argumentos. Un uso es pasar funciones globales envueltas por este adaptador a algoritmos STL. El tipo de retorno son las clases de plantilla std::pointer_to_unary_function o std::pointer_to_binary_function dependiendo de la cantidad de argumentos.
La función de plantilla std::bind se denomina carpeta y proporciona soporte para la aplicación de funciones parciales . Vincula algunos argumentos a un objeto de función, creando un nuevo objeto de función. Es decir, llamar a un enlazador es equivalente a llamar a un objeto de función con algunos parámetros definidos. Puede pasar al enlazador los valores de los argumentos directamente o nombres especiales definidos en el espacio de nombres std::placeholders que indican al enlazador que el argumento dado no se enlazará y determinará el orden de los argumentos en el objeto de función devuelto.
Esta función apareció por primera vez en la biblioteca Bind en Boost versión 1.25.0 [7] . Allí se posicionó como una generalización y extensión de los enlazadores estándar std::bind1st y std::bind2nd , ya que permitía enlazar un número arbitrario de argumentos y cambiar su orden. A partir de la revisión del estándar C++11 , bind se ha incluido en la biblioteca y los binders anteriores han quedado obsoletos.
Aquí f es el objeto llamado, bound_args es la lista de argumentos enlazados. El valor devuelto es un objeto de función de tipo T indefinido , que se puede colocar en una std::function y para el cual se ejecuta std::is_bind_expression<T>::value == true . Dentro, el envoltorio contiene un objeto de tipo std::decay<F>::type , creado con std::forward<F>(f) , así como un objeto para cada argumento del tipo similar std::decay< Arg_i>::tipo .
El espacio de nombres std::placeholders contiene los objetos especiales _1, _2, ... , _N , donde el número N depende de la implementación. Se utilizan en la función de vinculación para establecer el orden de los argumentos libres. Cuando dichos objetos se pasan como argumentos a la función de vinculación , se genera un objeto de función para ellos, en el que, cuando se llama con argumentos no vinculados, cada marcador de posición _N se reemplazará por el N-ésimo argumento no vinculado.
Se proporciona una clase de plantilla auxiliar std::is_placeholder para obtener un entero k del marcador de posición _K . Al pasarle un marcador de posición como parámetro de plantilla, es posible obtener un número entero al acceder a su campo de valor . Por ejemplo, is_placeholder<_3>::value devolverá 3.
La salida de este ejemplo será:
quinceEn la revisión de 1998 del estándar C++, la biblioteca estándar proporcionó los std::bind1st y std::bind2nd binders , que permitían convertir una función de dos argumentos en una función de un solo argumento vinculando el segundo argumento a algún valor. Toman un objeto de función y un valor de argumento para el enlace como entrada y devuelven clases de plantilla std::binder1st y std::binder2nd , herederos de unary_function , respectivamente.
ejemplo de uso
función vacía ( lista < int > & cont ) { list < int >:: const_iterator iter = find_if ( cont . begin (), cont . end (), bind2nd ( mayor < int > (), 10 )); // Trabaja un poco... }Un conjunto de objetos de función predefinidos para operaciones básicas ha sido una parte integral de la biblioteca de plantillas estándar desde su inicio en el estándar [2] . Estos son operadores aritméticos básicos ( +-*/% ), operadores lógicos básicos ( &&, ||, ! ) y operadores de comparación ( ==, !=, >, <, >=, <= ). A pesar de su trivialidad, estas clases se utilizaron para demostrar las capacidades de los algoritmos de la biblioteca estándar. Además, su presencia contribuye a la comodidad y ahorra al usuario de la biblioteca el trabajo redundante de escribir sus propios análogos [6] . Los funtores booleanos y de comparación son predicados y devuelven un tipo booleano . Desde C++11 [4] , también se han agregado algunas operaciones bit a bit ( and, or, xor, not ) .
Tipo de | Nombre | Número de operandos | tipo de retorno | Acción |
---|---|---|---|---|
comparaciones | igual a | Binario | bool | x == y |
no igual a | Binario | bool | x != y | |
mayor que | Binario | bool | x > y | |
menos | Binario | bool | x < y | |
mayor_igual | Binario | bool | x >= y | |
menos_igual | Binario | bool | x <= y | |
rompecabezas | lógica_y | Binario | bool | x & & y |
lógico_o | Binario | bool | x || y | |
lógico_no | unario | bool | !X | |
Aritmética | más | Binario | T | x+y |
menos | Binario | T | xy | |
multiplica | Binario | T | x*y | |
divide | Binario | T | x / y | |
módulo | Binario | T | x % y | |
negar | unario | T | -X | |
Bit a bit ( C++11 ) | bit_and | Binario | T | x&y |
bit_or | Binario | T | x | y | |
bit_xor | Binario | T | x^y | |
bit_not | unario | T | ~ x |
Además, junto con los predicados predefinidos, el archivo de encabezado contiene negadores de predicados que llaman al predicado y devuelven el resultado opuesto al resultado del predicado. Los predicados negadores son similares a los aglutinantes en el sentido de que toman una operación y producen otra operación a partir de ella. La biblioteca proporciona dos negadores de este tipo: unary not1() y binary not2() . El tipo de retorno de estos negadores son las clases auxiliares especiales unary_negate y binary_negate , definidas de la siguiente manera:
plantilla < clase Predicado > clase unary_negate { público : explícito unary_negate ( const Predicate & pred ); operador bool ()( const typename Predicate :: argument_type & x ) const ; }; plantilla < clase Predicado > clase binary_negate { público : explícito binary_negate ( const Predicate & pred ); operador bool ()( const typename Predicate :: first_argument_type & x , const typename Predicate :: second_argument_type & y ) const ;Aquí operator() devuelve !pred(x) en el primer caso y !pred(x,y) en el segundo. Un predicado unario debe tener un tipo de argumento específico , mientras que un predicado binario debe tener los tipos first_argument_type y second_argument_type . La presencia de dichas definiciones en clases como std::function , std::mem_fn y std::ref hace posible el uso de negadores junto con envoltorios de funciones.
En la versión original del estándar , unary_negate y binary_negate se derivaron de las clases base unary_function y binary_function , respectivamente, lo que permitió al usuario usar negadores para sus propios predicados. Dado que las clases base mencionadas anteriormente se marcaron como obsoletas y no hay reemplazo para los negadores que no sean las funciones lambda [11] , se decidió dejarlas.
El archivo de encabezado <funcional> define una pequeña clase auxiliar std::reference_wrapper , que envuelve una referencia a un objeto, o una referencia a una función, que se le pasa en la plantilla. Puede ser útil para pasar referencias a plantillas de funciones (por ejemplo, en algoritmos ), que normalmente hacen copias de objetos cuando se pasan por valor. Todo lo que hace reference_wrapper es almacenar una referencia al tipo T pasado en la plantilla y emitirla cuando se invoca al operador T&() .
La clase de plantilla reference_wrapper apareció por primera vez en la biblioteca Ref en Boost versión 1.25.0 [7] . Con algunas modificaciones, se incluyó en C++11.
Las funciones auxiliares ref y cref se proporcionan para crear objetos reference_wrapper , definidos de la siguiente manera:
template < class T > reference_wrapper < T > ref ( T & t ) noexcept ; template < class T > reference_wrapper < const T > cref ( const T & t ) noexcept ;