C++11

La versión actual de la página aún no ha sido revisada por colaboradores experimentados y puede diferir significativamente de la versión revisada el 15 de septiembre de 2020; las comprobaciones requieren 24 ediciones .

C++11 [1] [2] o ISO/IEC 14882:2011 [3] (en el proceso de trabajo en el estándar, tenía el nombre en clave C++0x [4] [5] ): una nueva versión de el estándar de lenguaje C++ en lugar del ISO /IEC 14882:2003 previamente válido. El nuevo estándar incluye adiciones al núcleo del lenguaje y una extensión a la biblioteca estándar, incluida la mayor parte de TR1  , excepto quizás la biblioteca de funciones matemáticas especiales. Las nuevas versiones de los estándares, junto con algunos otros documentos de estandarización de C++, se publican en el sitio web del comité ISO C++ [6] . Ejemplos de programación en C++

Los lenguajes de programación están experimentando un desarrollo gradual de sus capacidades (actualmente, después de C++11, se han publicado las siguientes extensiones estándar: C++14, C++17, C++20). Este proceso inevitablemente causa problemas de compatibilidad con el código existente. El Apéndice C.2 [diff.cpp03] del Borrador Final del Estándar Internacional N3290 describe algunas de  las incompatibilidades entre C++11 y C++03.

Cambios propuestos a la norma

Como ya se mencionó, los cambios afectarán tanto al núcleo de C++ como a su biblioteca estándar.

Al desarrollar cada sección de la futura norma, el comité utilizó una serie de reglas:

Se presta atención a los principiantes, que siempre serán la mayoría de los programadores. Muchos principiantes no buscan profundizar su conocimiento de C ++, limitándose a usarlo cuando trabajan en tareas específicas limitadas [7] . Además, dada la versatilidad de C++ y la amplitud de su uso (que incluye tanto la variedad de aplicaciones como los estilos de programación), incluso los profesionales pueden descubrir nuevos paradigmas de programación .

Extendiendo el lenguaje central

La tarea principal del comité es desarrollar el núcleo del lenguaje C++. El kernel se ha mejorado significativamente, se ha agregado soporte multiproceso , se ha mejorado el soporte para programación genérica , se ha unificado la inicialización y se ha trabajado para mejorar su rendimiento.

Para mayor comodidad, las características y los cambios del kernel se dividen en tres partes principales: mejoras de rendimiento, mejoras de conveniencia y nueva funcionalidad. Los elementos individuales pueden pertenecer a varios grupos, pero se describirán solo en uno: el más apropiado.

Mejora del rendimiento

Estos componentes de lenguaje se introducen para reducir la sobrecarga de memoria o mejorar el rendimiento.

Referencias a objetos temporales y semántica de movimiento

Según el estándar C++ , un objeto temporal resultante de la evaluación de una expresión se puede pasar a funciones, pero solo mediante una referencia constante ( const & ). La función no puede determinar si el objeto pasado se puede considerar temporal y modificable (un objeto const que también se puede pasar mediante dicha referencia no se puede modificar (legalmente)). Esto no es un problema para estructuras simples como complex, pero para tipos complejos que requieren asignación o desasignación de memoria, destruir un objeto temporal y crear uno permanente puede llevar mucho tiempo, mientras que uno podría simplemente pasar punteros directamente.

C++11 introduce un nuevo tipo de referencia , la referencia rvalue .  Su declaración es: tipo && . Las nuevas reglas de resolución de sobrecarga le permiten usar diferentes funciones sobrecargadas para objetos temporales no constantes, indicados por valores r, y para todos los demás objetos. Esta innovación permite la implementación de la llamada semántica de movimiento .

Por ejemplo, std::vector es un contenedor simple alrededor de una matriz C y una variable que almacena su tamaño. El constructor de copias std::vector::vector(const vector &x)creará una nueva matriz y copiará la información; el constructor de transferencia std::vector::vector(vector &&x)puede simplemente intercambiar punteros y variables que contengan la longitud.

Ejemplo de anuncio.

plantilla < clase T > vector de clase { vector ( const vector & ); // Copiar constructor (lento) vector ( vector && ); // Transferir el constructor desde un objeto temporal (rápido) vector & operator = ( const vector & ); // Asignación regular (lenta) vector & operador = ( vector && ); // Mover objeto temporal (rápido) void foo () & ; // Función que solo funciona en un objeto con nombre (lento) void foo () && ; // Función que funciona solo para un objeto temporal (rápido) };

Existen varios patrones asociados con los enlaces temporales, de los cuales los dos más importantes son y . El primero hace que un objeto con nombre regular sea una referencia temporal: moveforward

// std::mover plantilla ejemplo void bar ( std :: string && x ) { estándar estático :: stringsomeString ; _ algunaCadena = std :: mover ( x ); // dentro de la función x=string&, de ahí el segundo movimiento para llamar a la asignación de movimiento } std :: fibroso ; _ barra ( std :: mover ( y )); // el primer movimiento convierte string& en string&& para llamar a la barra

La plantilla se usa solo en metaprogramación, requiere un parámetro de plantilla explícito (tiene dos sobrecargas indistinguibles) y está asociada con dos nuevos mecanismos de C++. El primero es el pegado de enlaces: , luego . En segundo lugar, la función bar() anterior requiere un objeto temporal en el exterior, pero en el interior, el parámetro x es un nombre ordinario (lvalue) para el respaldo, lo que hace que sea imposible distinguir automáticamente el parámetro string& del parámetro string&&. En una función ordinaria sin plantilla, el programador puede o no poner move(), pero ¿qué pasa con la plantilla? forwardusing One=int&&; using Two=One&;Two=int&

// ejemplo de uso de la plantilla std::forward class Obj { std :: campo de cadena ; _ plantilla < claseT > _ Obj ( T && x ) : campo ( std :: adelante < T > ( x )) {} };

Este constructor cubre las sobrecargas normales (T=cadena&), copiar (T=cadena constante&) y mover (T=cadena) con pegado de referencia. Y forward no hace nada o se expande a std::move dependiendo del tipo de T, y el constructor copiará si es una copia y moverá si es un movimiento.

Expresiones constantes genéricas

C++ siempre ha tenido el concepto de expresiones constantes. Por lo tanto, expresiones como 3+4 siempre arrojaron los mismos resultados sin causar efectos secundarios. Por sí mismas, las expresiones constantes proporcionan una manera conveniente para que los compiladores de C++ optimicen el resultado de la compilación. Los compiladores evalúan los resultados de dichas expresiones solo en el momento de la compilación y almacenan los resultados ya calculados en el programa. Por lo tanto, tales expresiones se evalúan solo una vez. También hay algunos casos en los que el lenguaje estándar requiere el uso de expresiones constantes. Tales casos, por ejemplo, pueden ser definiciones de matrices externas o valores de enumeración.


int GiveFive () { return 5 ;} int some_value [ GiveFive () + 7 ]; // crea una matriz de 12 enteros; prohibido en C++

El código anterior es ilegal en C++ porque GiveFive() + 7 no es técnicamente una expresión constante conocida en tiempo de compilación. El compilador simplemente no sabe en ese momento que la función realmente devuelve una constante en tiempo de ejecución. El motivo de este razonamiento del compilador es que esta función puede afectar el estado de una variable global, llamar a otra función en tiempo de ejecución que no sea constante, etc.

C++11 introduce la palabra clave constexpr , que permite al usuario asegurarse de que una función o un constructor de objetos devuelva una constante de tiempo de compilación. El código anterior podría reescribirse así:

constexpr int GiveFive () { return 5 ;} int some_value [ GiveFive () + 7 ]; // crea una matriz de 12 enteros; permitido en C++11

Esta palabra clave permite que el compilador comprenda y verifique que GiveFive devuelve una constante.

El uso de constexpr impone restricciones muy estrictas sobre las acciones de la función:

  1. dicha función debe devolver un valor;
  2. el cuerpo de la función debe tener la forma return expresión ;
  3. la expresión debe constar de constantes y/o llamadas a otras funciones constexpr ;
  4. una función denotada por constexpr no se puede usar hasta que se defina en la unidad de compilación actual.

En la versión anterior del estándar, solo se podían usar variables de tipo entero o enumerado en expresiones constantes. En C++11, esta restricción se levanta para las variables cuya definición está precedida por la palabra clave constexpr:

constexpr doble aceleración de la gravedad = 9.8 ; constexpr double moonGravity = aceleracionDeLaGravedad / 6 ;

Dichas variables ya se consideran implícitamente indicadas por la palabra clave const . Solo pueden contener los resultados de expresiones constantes o los constructores de dichas expresiones.

Si es necesario construir valores constantes a partir de tipos definidos por el usuario, los constructores de dichos tipos también se pueden declarar mediante constexpr . Un constructor de expresiones constantes, como las funciones constantes, también debe definirse antes de su primer uso en la unidad de compilación actual. Dicho constructor debe tener un cuerpo vacío, y dicho constructor debe inicializar los miembros de su tipo solo con constantes.

Cambios en la definición de datos simples

En C++ estándar, solo las estructuras que satisfacen un determinado conjunto de reglas pueden considerarse un tipo de datos simple y antiguo ( POD). Hay buenas razones para esperar que estas reglas se amplíen para que más tipos se consideren POD. Los tipos que cumplen estas reglas se pueden usar en una implementación de capa de objetos compatible con C. Sin embargo, la lista de estas reglas de C++03 es demasiado restrictiva.

C++11 relajará varias reglas con respecto a la definición de tipos de datos simples.

Se considera que una clase es un tipo de datos simple si es trivial , tiene un diseño estándar ( distribución estándar ) y si los tipos de todos sus miembros de datos no estáticos también son tipos de datos simples.

Una clase trivial es una clase que:

  1. contiene un constructor predeterminado trivial,
  2. no contiene constructores de copias no triviales,
  3. no contiene constructores de movimientos no triviales,
  4. no contiene operadores de asignación de copia no triviales,
  5. no contiene operadores de asignación de movimiento no triviales,
  6. contiene un destructor trivial.

Una clase con colocación estándar es una clase que:

  1. no contiene miembros de datos no estáticos de un tipo de clase colocado de forma personalizada (o una matriz de elementos de ese tipo) o un tipo de referencia,
  2. no contiene funciones virtuales,
  3. no contiene clases base virtuales,
  4. tiene el mismo tipo de accesibilidad ( public, private, protected) para todos los miembros de datos no estáticos,
  5. no tiene clases base con ubicación no estándar,
  6. no es una clase que contiene simultáneamente miembros de datos no estáticos heredados y no heredados, o contiene miembros de datos no estáticos heredados de varias clases base a la vez,
  7. no tiene clases base del mismo tipo que el primer miembro de datos no estático (si lo hay).

Acelerar la compilación

Plantillas externas

En C++ estándar, el compilador debe instanciar una plantilla siempre que encuentre su especialización completa en una unidad de traducción. Esto puede aumentar significativamente el tiempo de compilación, especialmente cuando se crea una instancia de la plantilla con los mismos parámetros en una gran cantidad de unidades de traducción. Actualmente no hay forma de decirle a C++ que no debería haber instancias.

C++11 introdujo la idea de las plantillas externas. C++ ya tiene una sintaxis para decirle al compilador que se debe crear una instancia de una plantilla en un punto determinado:

clase de plantilla std :: vector < MiClase > ;

C++ carece de la capacidad de evitar que el compilador cree instancias de una plantilla en una unidad de traducción. C++11 simplemente extiende esta sintaxis:

clase de plantilla externa std :: vector < MyClass > ;

Esta expresión le dice al compilador que no cree una instancia de la plantilla en esta unidad de traducción.

Usabilidad mejorada

Estas características están destinadas a hacer que el lenguaje sea más fácil de usar. Le permiten fortalecer la seguridad de los tipos, minimizar la duplicación de código, dificultar el mal uso del código, etc.

Listas de inicialización

El concepto de listas de inicialización llegó a C++ desde C. La idea es que se puede crear una estructura o matriz pasando una lista de argumentos en el mismo orden en que se definen los miembros de la estructura. Las listas de inicialización son recursivas, lo que les permite usarse para arreglos de estructuras y estructuras que contienen estructuras anidadas.

objeto de estructura { flotar primero ; segundo int ; }; Objeto escalar = { 0.43f , 10 }; // un objeto, con first=0.43f y second=10 Object anArray [] = {{ 13.4f , 3 }, { 43.28f , 29 }, { 5.934f , 17 }}; // matriz de tres objetos

Las listas de inicialización son muy útiles para listas estáticas y cuando desea inicializar una estructura a un valor específico. C++ también contiene constructores, que pueden contener el trabajo general de inicializar objetos. El estándar C++ permite el uso de listas de inicialización para estructuras y clases, siempre que se ajusten a la definición Plain Old Data (POD). Las clases que no son POD no pueden usar listas de inicialización para la inicialización, incluidos los contenedores estándar de C++, como los vectores.

C++11 ha asociado el concepto de listas de inicialización y una clase de plantilla llamada std::initializer_list . Esto permitió que los constructores y otras funciones recibieran listas de inicialización como parámetros. Por ejemplo:

clase SequenceClass { público : SequenceClass ( std :: initializer_list < int > list ); };

Esta descripción le permite crear una SequenceClass a partir de una secuencia de enteros de la siguiente manera:

SequenceClass someVar = { 1 , 4 , 5 , 6 };

Esto demuestra cómo funciona un tipo especial de constructor para una lista de inicialización. Las clases que contienen dichos constructores se tratan de manera especial durante la inicialización (ver más abajo ).

La clase std::initializer_list<> está definida en la biblioteca estándar de C++11. Sin embargo, los objetos de esta clase solo pueden ser creados estáticamente por el compilador C++11 usando la sintaxis de corchetes {}. La lista se puede copiar después de la creación, sin embargo, esto será una copia por referencia. La lista de inicialización es constante: ni sus miembros ni sus datos se pueden cambiar después de la creación.

Debido a que std::initializer_list<> es un tipo completo, se puede usar en más que solo constructores. Las funciones ordinarias pueden tomar listas de inicialización escritas como argumento, por ejemplo:

void FunctionName ( std :: initializer_list < float > list ); Nombre de función ({ 1.0f , -3.45f , -0.4f });

Los contenedores estándar se pueden inicializar así:

std :: vector < std :: string > v = { "xyzzy" , "plugh" , "abracadabra" }; std :: vector < std :: string > v { "xyzzy" , "plugh" , "abracadabra" }; Inicialización genérica

El estándar C++ contiene una serie de problemas relacionados con la inicialización de tipos. Hay varias formas de inicializar tipos, y no todas conducen a los mismos resultados. Por ejemplo, la sintaxis tradicional de un constructor de inicialización puede parecer una declaración de función, y se debe tener especial cuidado para evitar que el compilador la analice incorrectamente. Solo los tipos agregados y los tipos POD se pueden inicializar con inicializadores agregados (del tipo SomeType var = {/*stuff*/};).

C ++ 11 proporciona una sintaxis que permite usar una forma única de inicialización para todo tipo de objetos al extender la sintaxis de la lista de inicialización:

struct EstructuraBásica { intx ; _ doble y ; }; estructura AltEstructura { AltStruct ( int x , doble y ) : x_ ( x ), y_ ( y ) {} privado : intx_ ; _ doble y_ ; }; EstructuraBásica var1 { 5 , 3.2 }; AltStruct var2 { 2 , 4.3 };

La inicialización de var1 funciona exactamente igual que la inicialización de agregados, es decir, cada objeto se inicializará copiando el valor correspondiente de la lista de inicialización. Si es necesario, se aplicará la conversión de tipo implícita. Si la transformación deseada no existe, el código fuente se considerará no válido. Durante la inicialización de var2 , se llamará al constructor.

Es posible escribir código como este:

estructura IdString { std :: nombre de cadena ; _ identificador int ; }; IdCadena ObtenerCadena ( ) { return { "AlgúnNombre" , 4 }; // Tenga en cuenta la falta de tipos explícitos }

La inicialización genérica no reemplaza completamente la sintaxis de inicialización del constructor. Si una clase tiene un constructor que toma una lista de inicialización ( TypeName(initializer_list<SomeType>); ) como argumento, tendrá prioridad sobre otras opciones de creación de objetos. Por ejemplo, en C++11 std::vector contiene un constructor que toma una lista de inicialización como argumento:

std :: vector < int > theVec { 4 };

Este código dará como resultado una llamada de constructor que toma una lista de inicialización como argumento, en lugar de un constructor de un parámetro que crea un contenedor del tamaño dado. Para llamar a este constructor, el usuario deberá usar la sintaxis de invocación del constructor estándar.

Inferencia de tipo

En C++ estándar (y C), el tipo de una variable debe especificarse explícitamente. Sin embargo, con la llegada de los tipos de plantillas y las técnicas de metaprogramación de plantillas, el tipo de algunos valores, especialmente los valores devueltos por funciones, no se puede especificar fácilmente. Esto genera dificultades para almacenar datos intermedios en variables, a veces puede ser necesario conocer la estructura interna de una biblioteca de metaprogramación en particular.

C++11 ofrece dos formas de mitigar estos problemas. Primero, la definición de una variable explícitamente inicializable puede contener la palabra clave auto . Esto dará como resultado la creación de una variable del tipo del valor de inicialización:

auto someStrangeCallableType = std :: bind ( & SomeFunction , _2 , _1 , someObject ); auto otra variable = 5 ;

El tipo someStrangeCallableType se convertirá en el tipo que devuelve la implementación concreta de la función de plantilla std::bindpara los argumentos proporcionados. El compilador determinará fácilmente este tipo durante el análisis semántico, pero el programador tendrá que investigar un poco para determinar el tipo.

El tipo otherVariable también está bien definido, pero el programador puede definirlo con la misma facilidad. Este tipo es int , lo mismo que una constante entera.

Además, la palabra clave decltype se puede usar para determinar el tipo de una expresión en tiempo de compilación . Por ejemplo:

int algoInt ; decltype ( someInt ) otherIntegerVariable = 5 ;

Usar decltype es más útil junto con auto , ya que el tipo de una variable declarada como auto solo lo conoce el compilador. Además, el uso de decltype puede ser bastante útil en expresiones que utilizan la sobrecarga de operadores y la especialización de plantillas.

autotambién se puede utilizar para reducir la redundancia de código. Por ejemplo, en lugar de:

for ( vector < int >:: const_iterator itr = myvec . cbegin (); itr != myvec . cend (); ++ itr )

el programador puede escribir:

for ( auto itr = myvec . cbegin (); itr != myvec . cend (); ++ itr )

La diferencia se vuelve especialmente notable cuando un programador usa una gran cantidad de contenedores diferentes, aunque todavía hay una buena manera de reducir el código redundante: usar typedef.

Un tipo marcado con decltype puede ser diferente del tipo inferido con auto .

#incluir <vector> int principal () { const std :: vector < int > v ( 1 ); automático a = v [ 0 ]; // escribe a - int decltype ( v [ 0 ]) b = 1 ; // escriba b - const int& (valor devuelto // std::vector<int>::operator[](size_type) const) auto c = 0 ; // tipo c - int auto d = c ; // escriba d - int decltype ( c ) e ; // tipo e - int, tipo de entidad llamada c decltype (( c )) f = c ; // el tipo f es int& porque (c) es un lvalue decltype ( 0 ) g ; // tipo g es int ya que 0 es un valor r } For-loop a través de una colección

En C++ estándar , iterar sobre los elementos de una colección requiere mucho código . Algunos lenguajes, como C# , tienen funciones que proporcionan una instrucción " foreach " que recorre automáticamente los elementos de una colección de principio a fin. C++11 presenta una instalación similar. La instrucción for facilita la iteración sobre una colección de elementos:

int my_array [ 5 ] = { 1 , 2 , 3 , 4 , 5 }; para ( int & x : mi_matriz ) { x *= 2 ; }

Esta forma de for, llamada "range-based for" en inglés, visitará cada elemento de la colección. Esto se aplicará a matrices C , listas de inicializadores y cualquier otro tipo que tenga funciones begin()y end()que devuelva iteradores . Todos los contenedores de la biblioteca estándar que tienen un par de inicio/fin funcionarán con una instrucción for en la colección.

Tal ciclo también funcionará, por ejemplo, con matrices tipo C, porque C++11 introduce artificialmente los pseudométodos necesarios para ellos (begin, end y algunos otros).

// recorrido basado en rangos de la matriz clásica int arr1 [] = { 1 , 2 , 3 }; para ( auto el : arr1 ); Funciones y expresiones lambda

En C++ estándar, por ejemplo, cuando se usa la biblioteca de algoritmos de C++ estándar sort and find , a menudo existe la necesidad de definir funciones de predicado cerca de donde se llama al algoritmo. Solo hay un mecanismo en el lenguaje para esto: la capacidad de definir una clase de functor (está prohibido pasar una instancia de una clase definida dentro de una función a los algoritmos (Meyers, STL efectivo)). A menudo, este método es demasiado redundante y detallado, y solo dificulta la lectura del código. Además, las reglas estándar de C++ para clases definidas en funciones no permiten su uso en plantillas y, por lo tanto, las hacen imposibles de usar.

La solución obvia al problema era permitir la definición de expresiones lambda y funciones lambda en C++11. La función lambda se define así:

[]( int x , int y ) { devuelve x + y ; }

El tipo de retorno de esta función sin nombre se calcula como decltype(x+y) . El tipo de valor devuelto solo se puede omitir si la función lambda tiene el formato . Esto limita el tamaño de la función lambda a una sola expresión. return expression

El tipo de devolución se puede especificar explícitamente, por ejemplo:

[]( int x , int y ) -> int { int z = x + y ; devuelve z ; }

Este ejemplo crea una variable temporal z para almacenar un valor intermedio. Al igual que con las funciones normales, este valor intermedio no se conserva entre llamadas.

El tipo de devolución se puede omitir por completo si la función no devuelve un valor (es decir, el tipo de devolución es nulo )

También es posible utilizar referencias a variables definidas en el mismo ámbito que la función lambda. Un conjunto de tales variables generalmente se denomina cierre . Los cierres se definen y utilizan de la siguiente manera:

std :: vector < int > algunaLista ; entero total = 0 ; std :: for_each ( algunaLista . comenzar (), algunaLista . final (), [ & total ]( int x ) { total += x ; }); std :: cout << total ;

Esto mostrará la suma de todos los elementos en la lista. La variable total se almacena como parte del cierre de la función lambda. Debido a que se refiere a la variable de pila total , puede cambiar su valor.

Las variables de cierre para las variables locales también se pueden definir sin usar el símbolo de referencia & , lo que significa que la función copiará el valor. Esto obliga al usuario a declarar su intención de hacer referencia o copiar una variable local.

Para las funciones lambda cuya ejecución está garantizada en su ámbito, es posible utilizar todas las variables de la pila sin necesidad de referencias explícitas a ellas:

std :: vector < int > algunaLista ; entero total = 0 ; std :: for_each ( algunaLista . comenzar (), algunaLista . fin (), [ & ]( int x ) { total += x ; });

Los métodos de implementación pueden variar internamente, pero se espera que la función lambda almacene un puntero a la pila de la función en la que se creó, en lugar de operar en referencias de variables de pila individuales.

[&]Si se usa en su lugar [=], se copiarán todas las variables usadas, lo que permitirá que la función lambda se use fuera del alcance de las variables originales.

El método de transferencia predeterminado también se puede complementar con una lista de variables individuales. Por ejemplo, si necesita pasar la mayoría de las variables por referencia y una por valor, puede usar la siguiente construcción:

entero total = 0 ; valor int = 5 ; [ & , valor ]( int x ) { total += ( x * valor ); } ( 1 ); //(1) llamar a la función lambda con valor 1

Esto hará que el total se pase por referencia y valor por valor.

Si una función lambda se define en un método de clase, se considera un amigo de esa clase. Tales funciones lambda pueden usar una referencia a un objeto del tipo de clase y acceder a sus campos internos:

[]( SomeType * typePtr ) { typePtr -> SomePrivateMemberFunction (); }

Esto solo funcionará si el alcance de la función lambda es un método de clase SomeType .

El trabajo con el puntero this al objeto con el que interactúa el método actual se implementa de una manera especial. Debe estar marcado explícitamente en la función lambda:

[ esto ]() { esto -> SomePrivateMemberFunction (); }

El uso de un formulario [&]o [=]una función lambda hace que esto esté disponible automáticamente.

El tipo de funciones lambda depende de la implementación; el nombre de este tipo está disponible solo para el compilador. Si necesita pasar una función lambda como parámetro, debe ser un tipo de plantilla o almacenarse mediante std::function . La palabra clave auto le permite guardar una función lambda localmente:

auto myLambdaFunc = [ this ]() { this -> SomePrivateMemberFunction (); };

Además, si la función no acepta argumentos, ()puede omitir:

auto myLambdaFunc = []{ std :: cout << "hola" << std :: endl ; }; Sintaxis de funciones alternativas

A veces es necesario implementar una plantilla de función que dé como resultado una expresión que tenga el mismo tipo y la misma categoría de valor que alguna otra expresión.

template < typename LHS , typename RHS > RETURN_TYPE AddingFunc ( const LHS & lhs , const RHS & rhs ) // ¿Qué debería ser RETURN_TYPE? { volver lhs + rhs ; }

Para que la expresión AddingFunc(x, y) tenga el mismo tipo y la misma categoría de valor que la expresión lhs + rhs cuando se le den los argumentos x e y , se podría usar la siguiente definición dentro de C++11:

template < typename LHS , typename RHS > decltype ( std :: declval < const LHS &> () + std :: declval < const RHS &> ()) AddingFunc ( const LHS & lhs , const RHS & rhs ) { volver lhs + rhs ; }

Esta notación es algo engorrosa, y sería bueno poder usar lhs y rhs en lugar de std::declval<const LHS &>() y std::declval<const RHS &>() respectivamente. Sin embargo, en la próxima versión

template < typename LHS , typename RHS > decltype ( lhs + rhs ) AddingFunc ( const LHS & lhs , const RHS & rhs ) // No válido en C++11 { volver lhs + rhs ; }

más legible por humanos, los identificadores lhs y rhs utilizados en el operando decltype no pueden indicar opciones declaradas más tarde. Para resolver este problema, C++11 introduce una nueva sintaxis para declarar funciones con un tipo de retorno al final:

template < typename LHS , typename RHS > auto AddingFunc ( const LHS & lhs , const RHS & rhs ) -> decltype ( lhs + rhs ) { volver lhs + rhs ; }

Cabe señalar, sin embargo, que en la implementación AddingFunc más genérica a continuación, la nueva sintaxis no se beneficia de la brevedad:

plantilla < nombre de tipo LHS , nombre de tipo RHS > Auto AddingFunc ( LHS && lhs , RHS && rhs ) - > decltype ( std :: adelante < LHS > ( lhs ) + std :: adelante < RHS > ( rhs )) { return std :: adelante < LHS > ( lhs ) + std :: adelante < RHS > ( rhs ); } plantilla < nombre de tipo LHS , nombre de tipo RHS > Auto AddingFunc ( LHS && lhs , RHS && rhs ) - > decltype ( std :: declval < LHS > () + std :: declval < RHS > ()) // mismo efecto que con std::forward arriba { return std :: adelante < LHS > ( lhs ) + std :: adelante < RHS > ( rhs ); } template < typename LHS , typename RHS > decltype ( std :: declval < LHS > () + std :: declval < RHS > ()) // mismo efecto que poner tipo al final AddingFunc ( LHS && lhs , RHS && rhs ) { return std :: adelante < LHS > ( lhs ) + std :: adelante < RHS > ( rhs ); }

La nueva sintaxis se puede usar en declaraciones y declaraciones más simples:

estructura AlgunaEstructura { auto FuncName ( int x , int y ) -> int ; }; auto SomeStruct :: FuncName ( int x , int y ) -> int { devuelve x + y _ }

El uso de la palabra clave " " autoen este caso significa solo una indicación tardía del tipo de retorno y no está relacionado con su inferencia automática.

Mejorando los constructores de objetos

C++ estándar no permite llamar a un constructor de clase desde otro constructor de la misma clase; cada constructor debe inicializar por completo a todos los miembros de la clase o llamar a los métodos de la clase para hacerlo. Los miembros no constantes de una clase no se pueden inicializar en el lugar donde se declaran esos miembros.

C++11 se deshace de estos problemas.

El nuevo estándar permite llamar a un constructor de clase desde otro (la llamada delegación). Esto le permite escribir constructores que usan el comportamiento de otros constructores sin introducir código duplicado.

Ejemplo:

clase AlgunTipo { número entero ; público : SomeType ( int new_number ) : number ( new_number ) {} AlgunTipo () : AlgunTipo ( 42 ) {} };

En el ejemplo, puede ver que el constructor SomeTypesin argumentos llama al constructor de la misma clase con un argumento entero para inicializar la variable number. Se podría lograr un efecto similar especificando un valor inicial de 42 para esta variable justo en su declaración.

clase AlgunTipo { numero entero = 42 ; público : AlgúnTipo () {} explícito SomeType ( int new_number ) : number ( new_number ) {} };

Cualquier constructor de clase se inicializará numbera 42 si no le asigna un valor diferente.

Java , C# y D son ejemplos de lenguajes que también solucionan estos problemas .

Cabe señalar que si en C++03 se considera que un objeto está completamente creado cuando su constructor completa la ejecución, entonces en C++11, después de que se haya ejecutado al menos un constructor delegado, el resto de los constructores trabajarán en un objeto completamente construido. A pesar de esto, los objetos de la clase derivada solo se construirán después de que se hayan ejecutado todos los constructores de las clases base.

Sustitución explícita de funciones virtuales y finalidad

Es posible que la firma de un método virtual se haya cambiado en la clase base o se haya configurado incorrectamente en la clase derivada inicialmente. En tales casos, el método dado en la clase derivada no anulará el método correspondiente en la clase base. Entonces, si el programador no cambia correctamente la firma del método en todas las clases derivadas, es posible que el método no se llame correctamente durante la ejecución del programa. Por ejemplo:

base de estructura { vacío virtual some_func (); }; estructura derivada : base { vacío sone_func (); };

Aquí, el nombre de una función virtual declarada en una clase derivada está mal escrito, por lo que dicha función no se anulará Base::some_funcy, por lo tanto, no se llamará polimórficamente a través de un puntero o referencia al subobjeto base.

C ++ 11 agregará la capacidad de rastrear estos problemas en tiempo de compilación (en lugar de tiempo de ejecución). Para compatibilidad con versiones anteriores, esta característica es opcional. La nueva sintaxis se muestra a continuación:

estructura B { vacío virtual some_func (); vacío virtual f ( int ); vacío virtual g () const ; }; estructura D1 : pública B { anular sone_func () anular ; // error: nombre de función no válido void f ( int ) override ; // OK: anula la misma función en la clase base virtual void f ( long ) override ; // error: el tipo de parámetro no coincide virtual void f ( int ) const override ; // error: función cv-calificación no coincide virtual int f ( int ) override ; // error: el tipo de retorno no coincide virtual void g () const final ; // OK: anula la misma función en la clase base virtual void g ( long ); // OK: nueva función virtual }; estructura D2 : D1 { vacío virtual g () const ; // error: intento de reemplazar la función final };

La presencia de un especificador para una función virtual finalsignifica que su reemplazo posterior es imposible. Además, una clase definida con el especificador final no se puede usar como clase base:

estructura F final { int x , y ; }; struct D : F // error: herencia de clases finales no permitida { intz ; _ };

Los identificadores overridey finaltienen un significado especial solo cuando se usan en ciertas situaciones. En otros casos, pueden usarse como identificadores normales (por ejemplo, como el nombre de una variable o función).

Constante de puntero nulo

Desde el advenimiento de C en 1972, la constante 0 ha desempeñado el doble papel de un número entero y un puntero nulo. Una forma de lidiar con esta ambigüedad inherente al lenguaje C es la macro NULL, que normalmente realiza la sustitución ((void*)0)o 0. C++ difiere de C en este aspecto, ya que solo permite el uso 0de un puntero nulo como constante. Esto conduce a una mala interacción con la sobrecarga de funciones:

foo vacío ( char * ); foo vacío ( int );

Si la macro NULLse define como 0(que es común en C++), la línea foo(NULL);resultará en una llamada foo(int), no foo(char *)como podría sugerir una mirada rápida al código, que casi con seguridad no es lo que pretendía el programador.

Una de las novedades de C++11 es una nueva palabra clave para describir una constante de puntero nulo - nullptr. Esta constante es de tipo std::nullptr_t, que puede convertirse implícitamente al tipo de cualquier puntero y compararse con cualquier puntero. No se permite la conversión implícita a un tipo integral, excepto para bool. La propuesta original del estándar no permitía la conversión implícita a booleanos, pero el grupo de redacción del estándar permitió tales conversiones en aras de la compatibilidad con los tipos de punteros convencionales. La redacción propuesta se modificó tras una votación unánime en junio de 2008 [1] .

Para compatibilidad con versiones anteriores, 0también se puede usar una constante como un puntero nulo.

char * pc = nullptr ; // verdadero int * pi = nullptr ; // verdadero bool b = nullptr ; // Correcto. b=falso. int i = nullptr ; // error foo ( nullptr ); // llama a foo(char *), no a foo(int);

A menudo, las construcciones en las que se garantiza que el puntero estará vacío son más simples y seguras que el resto, por lo que puede sobrecargarse con . nullptr_t

clase Carga útil ; clase SmartPtr { SmartPtr () = predeterminado ; SmartPtr ( nullptr_t ) {} // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< SmartPtr explícito ( Carga útil * aData ) : fData ( aDatos ) {} // copiar constructores y op= omitir ~ SmartPtr () { delete fData ; } privado : Carga útil * fData = nullptr ; } SmartPtr getPayload1 () { return nullptr ; } // Se llamará a la sobrecarga de SmartPtr(nullptr_t). Enumeraciones fuertemente tipadas

En C++ estándar, las enumeraciones no son seguras. De hecho, están representados por números enteros, a pesar de que los tipos de enumeraciones en sí son diferentes entre sí. Esto permite realizar comparaciones entre dos valores de diferentes enumeraciones. La única opción que ofrece C++03 para proteger las enumeraciones es no convertir implícitamente enteros o elementos de una enumeración en elementos de otra enumeración. Además, la forma en que se representa en la memoria (tipo entero) depende de la implementación y, por lo tanto, no es portátil. Finalmente, los elementos de enumeración tienen un alcance común, lo que hace imposible crear elementos con el mismo nombre en diferentes enumeraciones.

C++11 ofrece una clasificación especial de estas enumeraciones, libre de las desventajas anteriores. Para describir tales enumeraciones, se usa una declaración enum class(también se puede usar enum structcomo sinónimo):

enumeración de clase enum { Val1 , Val2 , Val3 = 100 , Val4 , /* = 101 */ };

Tal enumeración es de tipo seguro. Los elementos de una enumeración de clase no se pueden convertir implícitamente en enteros. Como consecuencia, la comparación con números enteros también es imposible (la expresión Enumeration::Val4 == 101da como resultado un error de compilación).

El tipo de enumeración de clase ahora es independiente de la implementación. De forma predeterminada, como en el caso anterior, este tipo es int, pero en otros casos, el tipo se puede configurar manualmente de la siguiente manera:

clase de enumeración Enum2 : int sin firmar { Val1 , Val2 };

El alcance de los miembros de la enumeración está determinado por el alcance del nombre de la enumeración. El uso de nombres de elementos requiere especificar el nombre de la enumeración de clase. Entonces, por ejemplo, el valor Enum2::Val1está definido, pero el valor Val1 no está definido.

Además, C ++ 11 ofrece la posibilidad de tipos subyacentes y de alcance explícitos para enumeraciones regulares:

enum Enum3 : largo sin firmar { Val1 = 1 , Val2 };

En este ejemplo, los nombres de los elementos de enumeración se definen en el espacio de enumeración (Enum3::Val1), pero por compatibilidad con versiones anteriores, los nombres de los elementos también están disponibles en el ámbito común.

También en C++ 11 es posible predeclarar enumeraciones. En versiones anteriores de C++, esto no era posible porque el tamaño de una enumeración dependía de sus elementos. Tales declaraciones solo se pueden usar cuando se especifica el tamaño de la enumeración (explícita o implícitamente):

enumeración Enum1 ; // no válido para C++ y C++11; el tipo subyacente no se puede determinar enum Enum2 : unsigned int ; // verdadero para C++11, tipo subyacente explícitamente especificado enum class Enum3 ; // verdadero para C++11, el tipo subyacente es int enum class Enum4 : unsigned int ; // verdadero para C++11. enum Enum2 : corto sin firmar ; // no válido para C++11 porque Enum2 se declaró previamente con un tipo subyacente diferente Paréntesis angulares

Los analizadores estándar de C++ siempre definen la combinación de caracteres ">>" como el operador de desplazamiento a la derecha. La ausencia de un espacio entre los corchetes angulares de cierre en los parámetros de la plantilla (si están anidados) se trata como un error de sintaxis.

C++11 mejora el comportamiento del analizador en este caso, de modo que varios corchetes angulares rectos se interpretarán como listas de argumentos de plantilla de cierre.

El comportamiento descrito se puede arreglar a favor del enfoque antiguo usando paréntesis.

plantilla < clase T > clase Y { /* ... */ }; Y < X < 1 >> x3 ; // Correcto, igual que "Y<X<1> > x3;". Y < X < 6 >> 1 >> x4 ; // Error de sintaxis. Debe escribir "Y<X<(6>>1)>> x4;".

Como se muestra arriba, este cambio no es totalmente compatible con el estándar anterior.

Operadores de conversión explícitos

El estándar de C++ proporciona la palabra clave explicitcomo modificador para los constructores de un parámetro para que dichos constructores no funcionen como constructores de conversión implícita. Sin embargo, esto no afecta de ninguna manera a los operadores de conversión reales. Por ejemplo, una clase de puntero inteligente podría contener operator bool()para imitar un puntero normal. Dicho operador se puede llamar, por ejemplo, así: if(smart_ptr_variable)(la rama se ejecuta si el puntero no es nulo). El problema es que dicho operador no protege contra otras conversiones inesperadas. Dado que el tipo boolse declara como un tipo aritmético en C++, es posible la conversión implícita a cualquier tipo entero o incluso a un tipo de punto flotante, lo que a su vez puede conducir a operaciones matemáticas inesperadas.

En C++11, la palabra clave explicittambién se aplica a los operadores de conversión. Al igual que los constructores, protege contra conversiones implícitas inesperadas. Sin embargo, las situaciones en las que el lenguaje espera contextualmente un tipo booleano (por ejemplo, en expresiones condicionales, bucles y operandos de operadores lógicos) se consideran conversiones explícitas y el operador de conversión booleano explícito se invoca directamente.

Plantilla typedef

En C++ estándar, una palabra clave typedefsolo se puede usar como una definición de sinónimo para otro tipo, incluso como sinónimo de una especificación de plantilla con todos sus parámetros especificados. Pero no es posible crear un sinónimo de plantilla. Por ejemplo:

plantilla < nombre de tipo Primero , nombre de tipo Segundo , int tercero > clase AlgúnTipo ; plantilla < nombre del tipo Segundo > _ typedef SomeType < OtherType , Second , 5 > TypedefName ; // No es posible en C++

Esto no compilará.

C++ 11 agregó esta capacidad con la siguiente sintaxis:

plantilla < nombre de tipo Primero , nombre de tipo Segundo , int tercero > clase AlgúnTipo ; plantilla < nombre del tipo Segundo > _ usando TypedefName = SomeType < OtherType , Second , 5 > ;

En C++ 11, la directiva usingtambién se puede usar para alias de un tipo de datos.

typedef void ( * OtroTipo )( doble ); // Estilo antiguo usando OtherType = void ( * )( double ); // Nueva sintaxis Eliminación de restricciones de la unión

En los estándares anteriores de C++, hay una serie de restricciones sobre el uso de miembros de tipos de clase dentro de uniones. En particular, las uniones no pueden contener objetos con un constructor no trivial. C++11 elimina algunas de estas restricciones. [2]

Aquí hay un ejemplo simple de una combinación que está permitida en C++ 11:

//para ubicación nueva #incluir <nueva> punto de estructura { _ Punto () {} Punto ( int x , int y ) : x_ ( x ), y_ ( y ) {} int x_ , y_ ; }; unión U { intz ; _ doble w ; Punto p ; // No es cierto para C++03 porque Point tiene un constructor no trivial. Sin embargo, el código funciona correctamente en C++11. U () { nuevo ( & p ) Punto (); } // No se definen métodos no triviales para la unión. // Si es necesario, se pueden eliminar para que la definición manual funcione };

Los cambios no afectan el código existente, ya que solo aflojan las restricciones existentes.

Extendiendo la funcionalidad

Esta sección describe nuevas funciones que antes no estaban disponibles o que requerían bibliotecas especiales no portátiles.

Plantillas de argumentos variables

Antes de C++ 11, las plantillas (de clases o funciones) solo podían tomar un número determinado de argumentos, definidos cuando la plantilla se declaró originalmente. C++11 le permite definir plantillas con un número variable de argumentos de cualquier tipo.

template < typename ... Values ​​> class tuple ;

Por ejemplo, la tupla de clase de plantilla ( tupla ) acepta cualquier número de nombres de tipo como parámetros de plantilla:

class tuple < int , std :: vector < int > , std :: map < std :: string , std :: vector < int >>> some_instance_name ;

Es posible que falten argumentos, por lo que la opción class tuple<> some_instance_nametambién funcionará.

Para evitar la creación de instancias de plantillas sin argumentos, se puede utilizar la siguiente definición:

template < typename Primero , typename ... Resto > class tuple ;

Las plantillas de argumentos variables también son aplicables a las funciones, lo que les permite usarse en variantes con seguridad de tipos de funciones variádicas (como printf) y para manejar objetos no triviales.

template < typename ... Params > void printf ( const std :: string & str_format , Params ... parámetros );

El operador ... juega aquí dos roles. A la izquierda de Params, un operador anuncia la necesidad de empaquetar parámetros. El uso de parámetros empaquetados permite asociar 0 o más argumentos con una plantilla. Los parámetros empaquetados se pueden usar para algo más que simplemente pasar nombres de tipos. El operador ... a la derecha, a su vez, desempaqueta los parámetros en argumentos separados (vea args...el cuerpo de la función en el ejemplo a continuación).

También es posible utilizar plantillas recursivamente con un número variable de argumentos. Un ejemplo sería el reemplazo de tipo seguro para printf :

void printf ( const char * s ) { mientras ( * s ) { si ( * s == '%' && * ( ++ s ) != '%' ) throw std :: runtime_error ( "cadena de formato no válido: faltan argumentos" ); estándar :: cout << * s ++ ; } } plantilla < nombre de tipo T , nombre de tipo ... Args > void printf ( const char * s , valor T , argumentos ... argumentos ) { mientras ( * s ) { si ( * s == '%' && * ( ++ s ) != '%' ) { std :: cout << valor ; ++ s ; printf ( s , argumentos ...); // continúa procesando argumentos incluso si *s == 0 return ; } estándar :: cout << * s ++ ; } throw std :: logic_error ( "argumentos adicionales proporcionados a printf" ); }

Este patrón es recursivo. Tenga en cuenta que la función printf llama a los resultados de instanciarse a sí misma oa la función base printf si args... está vacío.

No existe una manera fácil de omitir parámetros en una plantilla variádica. A pesar de esto, el uso del operador de desempaquetado de argumentos evita este problema.

Por ejemplo, una clase podría definirse así:

template < typename ... BaseClasses > class ClassName : public BaseClasses ... { público : ClassName ( BaseClasses && ... base_classes ) : BaseClasses ( base_classes )... {} };

El operador de desempaquetado duplicará todos los tipos de clases principales ClassNamede tal manera que la clase se heredará de todos los tipos especificados en los parámetros de la plantilla. Además, el constructor debe aceptar una referencia a todas las clases base para que se inicialice cada clase base principal ClassName.

Los parámetros de la plantilla se pueden redirigir. Combinado con referencias de rvalue (ver arriba), puede redirigir:

plantilla < nombre de tipo TypeToConstruct > struct SharedPtrAllocator { template < typename ... Args > std :: shared_ptr < TypeToConstruct > construct_with_shared_ptr ( Args && ... params ) { return std :: shared_ptr < TypeToConstruct > ( new TypeToConstruct ( std :: forward < Args > ( params )...)); }; };

Este código desempaqueta la lista de argumentos en el constructor TypeToConstruct. La sintaxis std::forward<Args>(params)le permite redirigir de manera absolutamente transparente los argumentos al constructor, independientemente de su naturaleza de valor. La función envuelve automáticamente los punteros std::shared_ptrpara brindar protección contra pérdidas de memoria.

También es posible especificar el número de argumentos empaquetados de la siguiente manera:

template < typename ... Args > struct SomeStruct { static const int size = sizeof ...( Args ); };

Aquí SomeStruct<Type1, Type2>::sizees igual a 2, e SomeStruct<>::sizeigual a 0.

Nuevos literales de cadena

C++03 ofrecía dos tipos de literales de cadena. El primer tipo, una cadena entre comillas dobles, es una matriz de tipo terminada en cero const char. El segundo tipo, definido como L"", es una matriz terminada en nulo de tipo const wchar_t, donde wchar_tes un carácter ancho de tamaños y semántica indeterminados. Ninguno de los tipos de literales está diseñado para admitir literales de cadena UTF-8 , UTF-16 o cualquier otro tipo de codificación Unicode

La definición de tipo charse ha modificado para decir explícitamente que tiene al menos el tamaño necesario para almacenar una codificación UTF-8 de ocho bits y lo suficientemente grande como para contener cualquier carácter en el conjunto de caracteres en tiempo de ejecución. Anteriormente en el estándar, este tipo se definió como un solo carácter; más tarde, siguiendo el estándar del lenguaje C, se garantizó que ocupara al menos 8 bits.

Hay tres codificaciones Unicode compatibles con el estándar C++11: UTF-8 , UTF-16 y UTF-32 . Además de los cambios anteriores al tipo de carácter incorporado char, C++11 agrega dos nuevos tipos de carácter: char16_ty char32_t. Están diseñados para almacenar caracteres UTF-16 y UTF-32, respectivamente.

A continuación se muestra cómo crear literales de cadena para cada una de estas codificaciones:

u8 "Soy una cadena UTF-8". u "Esta es una cadena UTF-16". U "Esta es una cadena UTF-32".

El tipo de la primera fila es normal const char[]. El tipo de la segunda línea es const char16_t[]. El tipo de la tercera línea es const char32_t[].

Al construir literales de cadena en el estándar Unicode, suele ser útil insertar el código Unicode directamente en la cadena. C++ 11 proporciona la siguiente sintaxis para esto:

u8 "Este es un carácter Unicode: \u2018 ". u "Este es un carácter Unicode más grande: \u2018 ". U "Este es un carácter Unicode: \U00002018 ".

El número posterior \udebe ser hexadecimal; no es necesario utilizar el prefijo 0x. El identificador \usignifica un código Unicode de 16 bits; para ingresar un código de \U32 bits, también se usa un número hexadecimal de 32 bits. Solo se pueden ingresar códigos Unicode válidos. Por ejemplo, los códigos en el rango U+D800-U+DFFF no están permitidos porque están reservados para pares sustitutos UTF-16.

A veces también es útil evitar el escape manual de cadenas, especialmente cuando se usan literales de archivos XML , lenguajes de secuencias de comandos o expresiones regulares. Para estos fines, C ++ 11 admite literales de cadena "en bruto":

R"(La cadena de datos \ cosas ")" R"delimitador(La Cadena de Datos \ Cosas " )delimitador"

En el primer caso, todo lo que está entre "(y )"es parte de la cadena. Los personajes "y \no necesitan ser escapados. En el segundo caso , "delimiter(comienza una cadena y solo termina cuando llega a )delimiter". La cadena delimiterpuede ser cualquier cadena de hasta 16 caracteres, incluida la cadena vacía. Esta cadena no puede contener espacios, caracteres de control, ' (', ' )' o el carácter ' \'. El uso de esta cadena delimitadora permite que el carácter ' )' se use en literales de cadena sin procesar. Por ejemplo, R"delimiter((a-z))delimiter"es equivalente a "(a-z)"[3] .

Los literales de cadena "sin procesar" se pueden combinar con un conjunto literal expandido (prefijo L"") o cualquier prefijo literal Unicode.

LR"(Literal de cadena ancha sin procesar \t (sin tabulación))" u8R"XXX(Soy una cadena "UTF-8 sin procesar")XXX" uR"*(Esta es una cadena "UTF-16 sin procesar")*" UR"(Esta es una cadena "UTF-32 sin procesar")" Literales personalizados

Los literales personalizados se implementan mediante la sobrecarga de operadores operator"". Los literales pueden ser calificadores en línea o constexpr . Es deseable que el literal comience con un carácter de subrayado, ya que puede haber un conflicto con estándares futuros. Por ejemplo, el literal i ya pertenece a los números complejos de std::complex.

Los literales pueden tomar sólo uno de los siguientes tipos: const char * , unsigned long long int , long double , char , wchar_t , char16_t , char32_t. Basta con sobrecargar el literal solo para el tipo const char * . Si no se encuentra un candidato más adecuado, se llamará a un operador con ese tipo. Un ejemplo de conversión de millas a kilómetros:

constexpr int operador "" _mi ( int largo largo sin signo i ) { retornar 1.6 * i ;}

Los literales de cadena toman un segundo argumento std::size_ty uno de los primeros: const char * , const wchar_t *, const char16_t * , const char32_t *. Los literales de cadena se aplican a las entradas entre comillas dobles.

Modelo de memoria multiproceso

C ++ 11 estandariza el soporte para la programación de subprocesos múltiples. Hay dos partes involucradas: un modelo de memoria que permite que múltiples subprocesos coexistan en un programa y una biblioteca que admite la comunicación entre subprocesos.

El modelo de memoria define cómo varios subprocesos pueden acceder a la misma ubicación de memoria y define cuándo los cambios realizados por un subproceso se vuelven visibles para otros subprocesos.

Almacenamiento en subprocesos Incumplimiento explícito y eliminación de métodos especiales

Los especificadores defaulty deletese pueden especificar en lugar del cuerpo del método.

clase Foo { público : foo () = predeterminado ; Foo ( int x ) { /* ... */ } };

El especificador defaultsignifica la implementación predeterminada y solo se puede aplicar a funciones miembro especiales:

  • Constructor predeterminado;
  • copiar constructor;
  • mover constructor;
  • operador de asignación;
  • mover operador;
  • incinerador de basuras.

El deleteespecificador marca aquellos métodos con los que no se puede trabajar. Anteriormente, tenía que declarar dichos constructores en el ámbito privado de la clase.

clase Foo { público : foo () = predeterminado ; Foo ( const Foo & ) = eliminar ; barra vacía ( int ) = eliminar ; barra vacía ( doble ) {} }; // ... Foo obj ; objeto _ barra ( 5 ); // ¡error! objeto _ barra ( 5.42 ); // OK Escriba long long int

El tipo entero long long intse especifica en C99 y se usa ampliamente de facto en C++. Ahora está incluido oficialmente en el estándar.

Diagnósticos estáticos

C++ 11 tiene dos mecanismos de diagnóstico estáticos:

  • La palabra clave static_assertarroja un error de compilación si la expresión entre paréntesis es falsa.
  • Una biblioteca type_traitsque contiene plantillas que proporcionan información de tipo en tiempo de compilación.
#incluir <tipo_rasgos> plantilla < claseT > _ ejecución nula ( T * aData , size_t n ) { static_assert ( std :: is_pod < T >:: value , "El tipo T debe ser simple." ); ... } Trabajar con miembros de datos de tamaño de clases sin crear un objeto

C ++ 03 permitió que el operador sizeofse usara en tipos y objetos simples. Pero la siguiente construcción no era válida:

struct SomeType { OtroTipo miembro ; }; sizeof ( SomeType :: miembro ); //No funciona en C++03, pero es cierto en C++11.

El resultado de esta llamada debe ser un tamaño OtherType. C++03 no admite dicha llamada y este código no se compilará. C++11 permite este tipo de construcciones.

Control de alineación de objetos y solicitudes de alineación

C++11 le permite alinear variables usando los operadores alignofy alignas.

alignoftoma un tipo y devuelve el número de bytes por los que se puede desplazar el objeto. Por ejemplo struct X { int n; char c; };, para 8 bytes, alignofdevolverá el valor 4. Para enlaces, devolverá el valor del tipo de enlace; para arreglos, el valor del elemento del arreglo

alignascontrola la alineación de un objeto en la memoria. Por ejemplo, puede especificar que una matriz de caracteres debe estar correctamente alineada para almacenar el tipo float:

alignas ( float ) carácter sin firmar c [ tamaño de ( float )] Permitir implementaciones con un recolector de basura Atributos

Cambios en la biblioteca estándar de C++

Cambios a componentes existentes

  • Cuando se pega, std::setel programador a veces sabe en qué posición terminará el nuevo elemento. Para esto, se usa un parámetro opcional: "pista"; si la conjetura es correcta, la estimación de tiempo será una constante amortizada, no O(log n) . Se cambió el significado de "pista" en C++11: antes significaba el elemento anterior al actual, lo cual no es del todo correcto: no está claro qué hacer si la inserción está en la primera posición. Ahora este es el elemento después del actual.
  • Se ha escrito una plantilla conveniente que llama a los constructores sin asignación de memoria: std::allocator_traits<>::construct(). Se ha agregado un método a todos los contenedores emplaceque crea un objeto en su lugar.
  • Se agregaron nuevas características del lenguaje C++11.
  • Métodos agregados cbeginy cendgarantizados para crear iteradores de const. Conveniente para la metaprogramación, para configurar tipos a través de auto.
  • En los contenedores que inician la memoria con un margen, ha aparecido una función shrink_to_fit.
  • B std::listpone límites más estrictos a lo que se hace en O ( n ) y lo que se hace en tiempo constante.
  • Se agregó std::vectoracceso directo a la memoria a través de data().
  • Prohibir que varios std::stringse refieran a la misma memoria. Gracias a esto, apareció el acceso directo a través front()de , que es conveniente, por ejemplo, para la interacción de string y WinAPI .

Control de flujo

Si bien el lenguaje C++03 proporciona un modelo de memoria que admite subprocesos múltiples, la biblioteca estándar C++11 proporciona el soporte principal para el uso real de subprocesos múltiples.

Se proporciona una clase de subproceso ( std::thread) que acepta un objeto de función (y una lista opcional de argumentos para pasarle) para ejecutar en un nuevo subproceso. Puede forzar la detención de un subproceso antes de que se complete otro subproceso en ejecución proporcionando soporte para la agrupación de subprocesos a través de una función miembro std::thread::join(). Si es posible, se proporciona acceso al identificador nativo del subproceso para operaciones específicas de la plataforma a través de la función miembro std::thread::native_handle().

Para la sincronización entre subprocesos, se agregan a la biblioteca los mutex apropiados ( std::mutex, std::recursive_mutexetc.) y las variables de condición ( std::condition_variabley ). std::condition_variable_anyEstán disponibles mediante bloqueos de inicialización de recursos (RAII) ( std::lock_guardy std::unique_lock) y algoritmos de bloqueo para facilitar su uso.

El trabajo de bajo nivel y alto rendimiento a veces requiere comunicación entre subprocesos sin la sobrecarga de mutexes. Esto se hace usando operaciones atómicas en ubicaciones de memoria. Opcionalmente, pueden especificar los límites mínimos de visibilidad de memoria requeridos para la operación. Las barreras de memoria explícitas también se pueden utilizar para este propósito.

La biblioteca de subprocesos de C++11 también incluye futuros y promesas para pasar resultados asincrónicos entre subprocesos y una clase std::packaged_taskpara envolver una llamada de función que puede generar dicho resultado asincrónico. La propuesta de futuros ha sido criticada porque carece de una forma de combinar futuros y verificar el cumplimiento de una sola promesa en un conjunto de promesas.

Las funciones adicionales de subprocesos de alto nivel, como los grupos de subprocesos, se han incluido en un documento técnico futuro de C++. No son parte de C++ 11, pero se espera que su implementación final se construya completamente sobre las características de la biblioteca de subprocesos.

La nueva función std::asyncproporciona una manera conveniente de ejecutar tareas y vincular el resultado de su ejecución a un objeto de std::future. El usuario puede elegir si el trabajo se ejecutará de forma asincrónica en un subproceso independiente o de forma sincrónica en el subproceso actual que espera el valor.

Tablas hash

std::hash_sety std::hash_maphan sido durante mucho tiempo una extensión STL no estándar, de hecho implementada en la mayoría de los compiladores. En C++11 se convirtieron en estándar, bajo los nombres std::unordered_sety std::unordered_map. Aunque en realidad son tablas hash y el estándar no deja mucho margen de maniobra, los nombres se dan en estilo C++: no "cómo se implementan", sino "qué son".

Expresiones regulares

La nueva biblioteca, declarada en el archivo de encabezado <regex>, contiene varias clases nuevas:

  • Las expresiones regulares se representan como instancias de std::regex;
  • los resultados de la búsqueda se representan como instancias de plantilla std::match_results.

La función std::regex_searchse usa para buscar, para la operación de 'buscar y reemplazar' se usa la función std::regex_replace. La función devolverá una cadena después de realizar el reemplazo. Los algoritmos std::regex_searchy std::regex_replacetoman una expresión regular y una cadena como entrada y devuelven los resultados encontrados como una instancia de std::match_results.

Ejemplo de uso std::match_results:

const char * reg_esp = "[ ,. \\ t \\ n;:]" ; // Lista de caracteres separadores. // lo mismo se puede hacer usando cadenas "en bruto": // const char *reg_esp = R"([ ,.\t\n;:])"; estándar :: expresión regular rgx ( reg_esp ); // 'regex' es una instancia de la clase de plantilla // 'basic_regex' con el parámetro de plantilla 'char'. std :: coincidencia cmatch ; // 'cmatch' es una instancia de la clase de plantilla // 'match_results' con el parámetro de plantilla 'const char *'. const char * target = "Universidad Invisible - Ankh-Morpork" ; // Corrige todas las palabras de la cadena 'objetivo' separadas por caracteres de 'reg_esp'. if ( std :: regex_search ( objetivo , coincidencia , rgx ) ) { // Si las palabras separadas por los caracteres dados están presentes en la cadena. const size_t n = coincidencia . tamaño (); para ( tamaño_t a = 0 ; a < n ; a ++ ) { std :: string str ( coincidencia [ a ]. primero , coincidencia [ a ]. segundo ); std :: cout << cadena << " \n " ; } }

Tenga en cuenta que se requieren barras invertidas dobles porque C ++ usa barras invertidas para escapar de los caracteres. Puede usar "cadenas sin procesar", otra innovación del estándar C ++ 11.

La biblioteca <regex>no requiere ninguna modificación de los archivos de encabezado existentes, ni la instalación de extensiones de idioma adicionales.


Clases de generación de números aleatorios extensibles

La biblioteca estándar de C permitió la generación de números pseudoaleatorios usando el rand. Sin embargo, su comportamiento puede variar dependiendo de la implementación.

Esta funcionalidad se divide en dos partes: el motor generador, que contiene el estado actual del generador de números aleatorios y produce números pseudoaleatorios, y la distribución, que determina el rango y la distribución matemática del resultado. La combinación de estos dos objetos crea un generador de números aleatorios.

Motores generadores:

Distribuciones:

Ejemplo:

#incluir <aleatorio> #incluir <funcional> std :: uniform_int_distribution < int > distribución ( 0 , 99 ); estándar :: motor mt19937 ; _ // Mersenne vortex MT19937 generador automático = std :: bind ( distribución , motor ); int aleatorio = generador (); // Obtenga un número aleatorio entre 0 y 99. int random2 = distribución ( motor ); // Obtenga un número aleatorio usando el motor y la distribución directamente.



Funciones planificadas no incluidas en el estándar

Módulos El gran volumen de archivos de encabezado condujo a un aumento cuadrático en el tiempo de compilación: tanto la cantidad de código como la cantidad de módulos en una sola unidad de compilación aumentan. Los módulos deben proporcionar un mecanismo similar a los archivos DCU de Delphi oa los archivos de clase Java .

Funciones eliminadas o obsoletas

Véase también

Notas

  1. Herb Sutter , Tenemos un estándar internacional: C++0x está aprobado por unanimidad . Archivado el 11 de diciembre de 2018 en Wayback Machine .
  2. Scott Meyers , Resumen de la disponibilidad de características de C++11 en gcc y MSVC Archivado el 26 de octubre de 2011 en Wayback Machine , 16 de agosto de 2011
  3. ISO , ISO/IEC 14882:2011 Archivado el 29 de enero de 2013 en Wayback Machine .
  4. Nombre de C++0x definido en el borrador final N3290 . Archivado el 20 de junio de 2010 en Wayback Machine .
  5. Stroustrup, Bjorn  - C++0x - el próximo estándar ISO C++. Archivado el 11 de mayo de 2011 en Wayback Machine .
  6. Documentos del Comité de Normas de C++ . Consultado el 24 de febrero de 2008. Archivado desde el original el 18 de marzo de 2010.
  7. La fuente de C++ Bjarne Stroustrup ( 2 de enero de 2006 ) Una breve mirada a C++0x . (Inglés)

Documentos del Comité de Estándares de C++

  •   Doc. núm. 1401: Jan Kristoffersen (21 de octubre de 2002)Operaciones atómicas con entornos de subprocesos múltiples
  •   Doc. núm. 1402: Doug Gregor (22 de octubre de 2002)Una propuesta para agregar un contenedor de objetos de función polimórfica a la biblioteca estándar
  •   Doc. núm. 1403: Doug Gregor (8 de noviembre de 2002)Propuesta para agregar tipos de tuplas a la biblioteca estándar
  •   Doc. núm. 1424: John Maddock (3 de marzo de 2003)Una propuesta para agregar rasgos de tipo a la biblioteca estándar
  •   Doc. núm. 1429: John Maddock (3 de marzo de 2003)Una propuesta para agregar expresiones regulares a la biblioteca estándar
  •   Doc. núm. 1449: B. Stroustrup, G. Dos Reis, Mat Marcus, Walter E. Brown, Herb Sutter (7 de abril de 2003)Propuesta para agregar alias de plantilla a C++
  •   Doc. núm. 1450: P. Dimov, B. Dawes, G. Colvin (27 de marzo de 2003)Una propuesta para agregar punteros inteligentes de propósito general al informe técnico de la biblioteca (Revisión 1)
  •   Doc. núm. 1452: Jens Maurer (10 de abril de 2003)Una propuesta para agregar una función extensible de números aleatorios a la biblioteca estándar (Revisión 2)
  •   Doc. núm. 1453: D. Gregor, P. Dimov (9 de abril de 2003)Una propuesta para agregar un contenedor de referencia a la biblioteca estándar (revisión 1)
  •   Doc. núm. 1454: Douglas Gregor, P. Dimov (9 de abril de 2003)Un método uniforme para calcular tipos de retorno de objetos de función (revisión 1)
  •   Doc. núm. 1456: Matthew Austern (9 de abril de 2003)Una propuesta para agregar tablas hash a la biblioteca estándar (revisión 4)
  •   Doc. núm. 1471: Daveed Vandevoorde (18 de abril de 2003)Metaprogramación reflexiva en C++
  •   Doc. núm. 1676: Bronek Kozicki (9 de septiembre de 2004)Operador de asignación de copia sobrecargada no miembro
  •   Doc. núm. 1704: Douglas Gregor, Jaakko Järvi, Gary Powell (10 de septiembre de 2004)Plantillas Variadic: Explorando el espacio de diseño
  •   Doc. núm. 1705: J. Järvi, B. Stroustrup, D. Gregor, J. Siek, G. Dos Reis (12 de septiembre de 2004)Decltype (y auto)
  •   Doc. núm. 1717: Francis Glassborow, Lois Goldthwaite (5 de noviembre de 2004)definiciones explícitas de clase y predeterminadas
  •   Doc. núm. 1719: Herb Sutter, David E. Miller (21 de octubre de 2004)Enumeraciones fuertemente tipadas (revisión 1)
  •   Doc. núm. 1720: R. Klarer, J. Maddock, B. Dawes, H. Hinnant (20 de octubre de 2004)Propuesta para agregar afirmaciones estáticas al lenguaje central (Revisión 3)
  •   Doc. núm. 1757: Daveed Vandevoorde (14 de enero de 2005)Soportes de ángulo recto (Revisión 2)
  •   Doc. núm. 1811: J. Stephen Adamczyk (29 de abril de 2005)Adición del tipo long long a C++ (Revisión 3)
  •   Doc. núm. 1815: Lawrence Crowl (2 de mayo de 2005)Plan estratégico ISO C ++ para subprocesos múltiples
  •   Doc. núm. 1827: Chris Uzdavinis, Alisdair Meredith (29 de agosto de 2005)Una sintaxis de invalidación explícita para C++
  •   Doc. núm. 1834: Detlef Vollmann (24 de junio de 2005)Alegato de soporte de procesamiento paralelo razonable en C++
  •   Doc. núm. 1836: ISO/IEC DTR 19768 (24 de junio de 2005)Borrador de informe técnico sobre extensiones de biblioteca de C++
  •   Doc. núm. 1886: Gabriel Dos Reis, Bjarne Stroustrup (20 de octubre de 2005)Especificación de conceptos de C++
  •   Doc. núm. 1891: Walter E. Brown (18 de octubre de 2005)Progreso hacia definiciones de tipos opacos para C++ 0X
  •   Doc. núm. 1898: Michel Michaud, Michael Wong (6 de octubre de 2004)Reenviadores y constructores heredados
  •   Doc. núm. 1919: Bjarne Stroustrup, Gabriel Dos Reis (11 de diciembre de 2005)Listas de inicialización
  •   Doc. núm. 1968: V Samko J Willcock, J Järvi, D Gregor, A Lumsdaine (26 de febrero de 2006)Expresiones lambda y cierres para C++
  •   Doc. núm. 1986: Herb Sutter, Francis Glassborow (6 de abril de 2006)Delegating Constructors (revisión 3)
  •   Doc. núm. 2016: Hans Boehm, Nick Maclaren (21 de abril de 2002)¿Debería volatile adquirir semántica de atomicidad y visibilidad de subprocesos?
  •   Doc. núm. 2142: ISO/IEC DTR 19768 (12 de enero de 2007)Estado de la evolución de C++ (entre las reuniones de Portland y Oxford de 2007)
  •   Doc. núm. 2228: ISO/IEC DTR 19768 (3 de mayo de 2007)Estado de la evolución de C++ (Oxford 2007 Meetings)
  •   Doc. núm. 2258: G. Dos Reis y B. StroustrupPlantillas Alias
  •   Doc. núm. 2280: Lawrence Crowl (2 de mayo de 2007)Almacenamiento local de subprocesos
  •   Doc. núm. 2291: ISO/IEC DTR 19768 (25 de junio de 2007)Estado de la evolución de C++ (reuniones de Toronto de 2007)
  •   Doc. núm. 2336: ISO/IEC DTR 19768 (29 de julio de 2007)Estado de la evolución de C++ (reuniones de Toronto de 2007)
  •   Doc. núm. 2389: ISO/IEC DTR 19768 (7 de agosto de 2007)Estado de la evolución de C++ (reuniones anteriores a Kona 2007)
  •   Doc. núm. 2431: SC22/WG21/N2431 = J16/07-0301 (2 de octubre de 2007),un nombre para el puntero nulo: nullptr
  •   Doc. núm. 2432: ISO/IEC DTR 19768 (23 de octubre de 2007)Estado de la evolución de C++ (reunión posterior a Kona 2007)
  •   Doc. núm. 2437: Lois Goldthwaite (5 de octubre de 2007)Operadores de conversión explícitos
  •   Doc. núm. 2461: ISO/IEC DTR 19768 (22 de octubre de 2007)Borrador de trabajo, Estándar para el lenguaje de programación C++
  •   Doc. núm. 2507: ISO/IEC DTR 19768 (4 de febrero de 2008)Estado de la evolución de C++ (reunión anterior a Bellevue 2008)
  •   Doc. núm. 2544: Alan Talbot, Lois Goldthwaite, Lawrence Crowl, Jens Maurer (29 de febrero de 2008)Uniones sin restricciones
  •   Doc. núm. 2565: ISO/IEC DTR 19768 (7 de marzo de 2008)Estado de la evolución de C++ (reunión posterior a Bellevue 2008)
  •   Doc. núm. 2597: ISO/IEC DTR 19768 (29 de abril de 2008)Estado de la evolución de C++ (reunión anterior a Antipolis 2008)
  •   Doc. núm. 2606: ISO/IEC DTR 19768 (19 de mayo de 2008)Borrador de trabajo, Estándar para el lenguaje de programación C++
  •   Doc. núm. 2697: ISO/IEC DTR 19768 (15 de junio de 2008)Minutas de la reunión del WG21 del 8 al 15 de junio de 2008
  •   Doc. núm. 2798: ISO/IEC DTR 19768 (4 de octubre de 2008)Borrador de trabajo, Estándar para el lenguaje de programación C++
  •   Doc. núm. 2857: ISO/IEC DTR 19768 (23 de marzo de 2009)Borrador de trabajo, Estándar para el lenguaje de programación C++
  •   Doc. núm. 2869: ISO/IEC DTR 19768 (28 de abril de 2009)Estado de la evolución de C++ (reunión posterior a San Francisco 2008)
  •   Doc. núm. 3000: ISO/ISC DTR 19769 (9 de noviembre de 2009)Borrador de trabajo, Estándar para el lenguaje de programación C++
  •   Doc. núm. 3014: Stephen D. Clamage (4 de noviembre de 2009)ORDEN DEL DÍA, PL22.16 Reunión No. 53, Reunión GT21 No. 48, 8-13 de marzo de 2010, Pittsburgh, Pensilvania
  •   Doc. núm. 3082: Herb Sutter (13 de marzo de 2010)Calendario de reuniones C++0x
  •   Doc. núm. 3092: ISO/ISC DTR 19769 (26 de marzo de 2010)Borrador de trabajo, Estándar para el lenguaje de programación C++
  •   Doc. núm. 3126: ISO/ISC DTR 19769 (21 de agosto de 2010)Borrador de trabajo, Estándar para el lenguaje de programación C++
  •   Doc. núm. 3225: ISO/ISC DTR 19769 (27 de noviembre de 2010)Borrador de trabajo, Estándar para el lenguaje de programación C++
  •   Doc. núm. 3242: ISO/ISC DTR 19769 (28 de febrero de 2011)Borrador de trabajo, Estándar para el lenguaje de programación C++
  •   Doc. núm. 3291: ISO/ISC DTR 19769 (5 de abril de 2011)Borrador de trabajo, Estándar para el lenguaje de programación C++
  •   Doc. núm. 3290: ISO/ISC DTR 19769 (5 de abril de 2011)FDIS, estándar para el lenguaje de programación C++
  •   Doc. núm. 3337 : Fecha: 2012-01-16 Borrador de trabajo, Estándar para el lenguaje de programación C++

Enlaces

Literatura

  • Stanley B. Lippman, Josy Lajoye, Barbara E. Moo. Lenguaje de programación C++. Curso básico 5.ª edición = C++ Primer (5.ª edición). - M. : "Williams" , 2014. - 1120 p. - ISBN 978-5-8459-1839-0 .
  • Siddhartha Rao. Enséñese usted mismo C++ en 21 días, 7.ª edición = Sams Enséñese usted mismo C++ en una hora al día, 7.ª edición. - M. : "Williams" , 2013. - 688 p. — ISBN 978-5-8459-1825-3 .
  • Esteban Prata. Lenguaje de programación C++ (C++11). Clases y ejercicios, 6.ª edición = C++ Primer Plus, 6.ª edición (Biblioteca para desarrolladores). - M. : "Williams" , 2012. - 1248 p. - ISBN 978-5-8459-1778-2 .