Ir al contenido

C++11

De Wikipedia, la enciclopedia libre

C++11 es una versión del lenguaje de programación C++ estándar aprobado por la Organización Internacional de Normalización (ISO) el 12 de agosto de 2011, reemplazando al anterior C++03. A partir del 18 de agosto de 2014 fue sustituido por la versión C++14 y más tarde por C++17. El nombre sigue la tradición de denominar a las versiones del lenguaje C++ a partir de la fecha de publicación, aunque su nombre original fue C++0x debido a que esperaba ser publicada antes de 2010.

A pesar de que uno de los objetivos era hacer los cambios en las librerías en lugar de hacerlo en el núcleo del lenguaje, C++11 añade algunas cosas al núcleo. Algunas áreas del lenguaje que fueron significativamente mejoradas como el soporte multihilo, soporte para la programación genérica, inicialización uniforme y el rendimiento. La Librería Estándar de C++ también recibió numerosos cambios, incorporando la mayoría de las bibliotecas definidas en el documento C++ Technical Report 1 (TR1), con la excepción de la biblioteca de funciones matemáticas especiales.

El borrador más parecido al C++11 publicado es el N3337 del 16 de enero de 2012, el cual realiza solo algunas correcciones editoriales al estándar.

Directivas de diseño

[editar]

En el desarrollo de cada utilidad del nuevo estándar, el comité ha aplicado algunas directivas:

  • Mantener la estabilidad y la compatibilidad con C++98 y posiblemente con C.
  • Preferir la introducción de nuevas características a través de la biblioteca estándar, en lugar de modificar el núcleo del lenguaje.
  • Preferir los cambios que puedan hacer evolucionar las técnicas de programación.
  • Mejorar C++ para facilitar el diseño de sistemas y bibliotecas, en vez de introducir nuevas características solamente útiles a aplicaciones específicas.
  • Incrementar la seguridad de los tipos de datos suministrando alternativas más seguras a las técnicas inseguras anteriores.
  • Incrementar el rendimiento y la habilidad de trabajar directamente con hardware.
  • Proveer soluciones apropiadas para los problemas del mundo real.
  • Implementar el principio de zero-overhead (soporte adicional requerido por algunas utilidades debe ser usado solo si la utilidad es usada).
  • Hacer que C++ sea más fácil de enseñar y aprender sin la remoción de cualquier utilidad necesaria por los programadores expertos.

La atención a los principiantes es muy importante porque ellos siempre serán la mayoría entre los programadores, y porque muchos principiantes no intentarían ampliar su conocimiento de C++, limitándose ellos mismos a operar en los aspectos del lenguaje en el que están especializados. Adicionalmente, considerando la vastedad de C++ y de su uso (incluyendo áreas de aplicación y estilos de programación), incluso los programadores más experimentados pueden convertirse en principiantes en un nuevo paradigma de programación.

Extensiones al núcleo del lenguaje

[editar]

Una función del comité de C++ es el desarrollo del núcleo del lenguaje. Las áreas del núcleo del lenguaje que fueron significativamente mejoradas incluyen el soporte multithreading (multihilo), el soporte de programación genérica, la inicialización uniforme, y mejoras del rendimiento.

Para los propósitos de este artículo, las características y los cambios del núcleo del lenguaje son agrupadas en 4 secciones generales:

Algunas características podrían entrar en múltiples grupos, pero solo son mencionadas en el grupo que primariamente representa esa característica.

Mejoras al rendimiento en tiempo de ejecución

[editar]

Estas características del lenguaje existen primariamente para proporcionar un cierto tipo de beneficio en el rendimiento, tanto de la memoria como de la velocidad cómputo.

  • Referencias rvalue y los move constructor
  • Expresiones constantes generalizadas
  • Modificación a la definición de plain old data

Referencias rvalue y los move constructor

[editar]

En el C++03 (y anteriores), los temporales, llamados "rvalues" pues a menudo se encuentran en el lado derecho de una asignación, fueron pensados para nunca ser modificables - justo como en C (y eran considerados ser indistinguibles de los tipos const T&) - aunque en algunos casos los temporales habrían podido ser modificados, e incluso era considerados ser un útil agujero de escape (loophole) (para el anterior, ver Sutter, Alexandrescu "C++ coding standards" #15). C++11 agrega un nuevo tipo de referencia no-constante llamado una referencia rvalue, identificada por T&&. Esto refiere a los temporales en que se permite que sean modificados después de que son inicializados, con el propósito de permitir la "semántica del movimiento" (move semantics).

Un problema de rendimiento crónico con el C++03 son las costosas e innecesarias copias profundas que pueden suceder implícitamente cuando los objetos son pasados por valor. Para ilustrar el problema, considere que un std::vector<T> es, internamente, una envoltura (wrapper) alrededor de una tabla (array) de estilo C con un tamaño. Si un std::vector<T> temporal se crea en una función, solamente se puede almacenar creando un nuevo std::vector<T> y copiando todos los datos rvalue en él. Luego, el temporal y toda su memoria es destruido. (Por simplicidad, este argumento deja de lado la optimización del valor devuelto).

En el C++11, un "move constructor" del std::vector<T> que toma una referencia rvalue a un std::vector<T> puede simplemente copiar el puntero a la variable interna, de estilo C, fuera del rvalue en el nuevo std::vector<T>, entonces cambiar el puntero dentro del rvalue a null. Debido a que se puede reinicializar el puntero temporal, el objeto no se borra cuando se sale del ámbito (scope), y no es necesaria una copia profunda. Por otra parte, es una operación segura e invisible porque el temporal nunca se usará otra vez.

Las referencias de rvalue pueden proporcionar beneficios de rendimiento al código existente sin la necesidad realizar cualquier cambio fuera de la biblioteca estándar. El tipo del valor retornado de una función que retorna un std::vector<T> temporal no necesita ser cambiado explícitamente al std::vector<T> && para invocar al move constructor, pues los temporales son considerados automáticamente como rvalues. (Sin embargo, si std::vector<T> es la versión del C++03 sin un move constructor, después el copy constructor será invocado con un const std::vector<T>& como se hace normalmente, incurriendo en una significativa asignación de memoria).

Por razones de la seguridad, algunas restricciones son impuestas. Una variable nombrada nunca será considerada para ser un rvalue incluso si se ha declarado como tal; para conseguir un rvalue, la plantilla de función std::move<T> () debe ser usada. También, las referencias de rvalue solamente pueden ser modificadas bajo ciertas circunstancias, siendo pensado para ser usado primariamente con los move constructor.

Debido a la naturaleza de la fraseología de las referencias rvalue, y a una cierta modificación a la fraseología para las referencias del lvalue (referencias regulares), las referencias rvalue permiten a los desarrolladores proporcionar forwarding de funciones perfectos. Cuando se combina con las plantillas variadic, esta capacidad permite plantillas de función que pueden perfectamente remitir (forward) argumentos a otra función que tome esos argumentos particulares. Esto es más útil para forwarding de parámetros del constructor, para crear funciones de factoría que llamarán automáticamente al constructor correcto para los particulares argumentos.

Expresiones constantes generalizadas

[editar]

El C++ siempre ha tenido el concepto de expresiones constantes. Estas son expresiones tales como 3+4 que darán siempre los mismos resultados, en tiempo de compilación y en tiempo de ejecución. Las expresiones constantes son oportunidades de optimización para los compiladores, y los compiladores frecuentemente las ejecutan en tiempo de compilación y luego ponen los resultados codificados directamente en el programa (hard coded). También, hay un número de lugares en donde la especificación del C++ requiere el uso de expresiones constantes. La definición de un arreglo requiere una expresión constante, y los valores del enumerador deben ser expresiones constantes.

Sin embargo, las expresiones constantes siempre han terminado cuando una llamada de función o un constructor de objeto sea encontrado. Así que una pieza de código tan simple como ésta es ilegal:

int dame_cinco() {return 5;}
int algun_valor[dame_cinco() + 7]; // Crea un arreglo de 12 enteros. Ilegal en C++

Éste no es C++ legal, porque dame_cinco() + 7 no es una expresión constante. El compilador no tiene manera de saber si dame_cinco() es realmente constante en tiempo de ejecución. En teoría, esta función podía afectar a una variable global, llamar a otras funciones constantes de no tiempo de ejecución, etc.

C++11 introdujo la palabra clave constexpr (expresión constante), que permite al usuario garantizar que una función o un constructor de objeto es constante en tiempo de compilación. El ejemplo de arriba puede ser reescrito como sigue:

constexpr int dame_cinco() {return 5;}
int algun_valor[dame_cinco() + 7]; // Crea un arreglo de 12 enteros. Legal en C++11

Esto permite que el compilador entienda, y verifique, que dame_cinco es una constante en tiempo de compilación.

El uso del constexpr en una función impone limitaciones muy estrictas en lo que esa función puede hacer. Primero, la función debe tener un tipo de retorno que no sea void. En segundo lugar, el contenido de la función debe ser de la forma: return expresión. Tercero, la expresión debe ser una expresión de constante después de la sustitución del argumento. Esta expresión constante puede solamente llamar a otras funciones definidas como constexpr, o puede usar otras variables de datos de expresiones constantes. Por último, una función con esta etiqueta no puede ser llamada hasta que es definida en esta unidad de traducción (translation unit).

Las variables también pueden ser definidas como valores de expresiones constantes:

constexpr double aceleracion_de_gravedad = 9.8;
constexpr double gravedad_lunar = aceleracion_de_gravedad / 6.0;

Las variables de los datos de la expresión constante son implícitmente const. Solo pueden almacenar los resultados de expresiones constantes o de constructores de expresiones constantes.

Para construir valores de datos de expresión constante desde tipos definidos por el usurario, los constructores también pueden ser declarados con constexpr. Un constructor de expresión constante debe ser definido antes de su uso en la unidad de traducción, como con las funciones de expresión constante. Debe tener un cuerpo de función vacío. Debe inicializar a sus miembros con expresiones constantes. Los destructores para tales tipos deben ser triviales.

Para copiar tipos construidos constexpr también deben ser definidos como constexpr, para permitir que retornen por valor desde una función constexpr. Cualquier función miembro de una clase, tal como copy constructors, sobrecarga de operadores, etc, pueden ser declaradas como constexpr, siempre y cuando adapten la definición para expresiones constantes de función. Esto permite al compilador copiar clases en tiempo de compilación, realiza operaciones en ellas, etc.

Una función de expresión constante o un constructor puede ser llamado con parámetros que no sean constexpr. Justo como un literal de número entero constexpr puede ser asignado a una variable que no sea constexpr, así que una función constexpr también puede ser llamada con parámetros que no sean constexpr, y los resultados almacenados en variables que no sean constexpr. La palabra clave solo permite la posibilidad de constante de tiempo de compilación cuando todos los miembros de una expresión son constexpr.

Modificación a la definición de plain old data

[editar]

En el C++03, una clase o struct debe seguir un número de reglas para que sea considerado un tipo plain old data (POD). Los tipos que cumplen esta definición producen layouts de objetos que son compatibles con C. Sin embargo, la definición en C++03 es innecesariamente estricta y hay buenas razones para permitir a más tipos cumplir la definición de POD [cita requerida]. C++11 relajó varias de las reglas [cita requerida].

Una clase/struct es considerada una POD si es trivial, de standard-layout, y si todos sus miembros no estáticos son POD.

Una clase o struct trivial se define como uno los siguientes:

  1. Tiene un constructor trivial por defecto. Éste puede usar la sintaxis de constructor por defecto (AlgunConstructor () = default;)
  2. Tiene constructores copy y move triviales, que pueden usar la sintaxis por defecto
  3. Tiene operadores de asignación copy y move triviales, los cuales pueden usar la sintaxis por defecto
  4. Tiene un destructor trivial, que no debe ser virtual

Una clase o struct es standard-layout, por definición, con tal que cumpla lo siguiente:

  1. No tenga funciones virtuales
  2. No tenga clases base virtuales
  3. No tenga clases base del mismo tipo que el primer miembro de datos definido no estático
  4. Todos sus miembros de datos no estáticos tienen el mismo control de acceso (público, privado, protegido)
  5. Todos sus miembros de datos no estáticos, incluyendo cualquiera en sus clases base, están en la misma clase en la jerarquía
  6. Las reglas antedichas también se aplican a todas las clases base y a todos los miembros de datos no estáticos en la jerarquía

Mejoras en el rendimiento del núcleo del lenguaje en tiempo de compilación

[editar]

Plantilla Extern

[editar]

En el C++03, el compilador debe instanciar una plantilla siempre que una plantilla especificada completamente es encontrada en una unidad de traducción. Si la plantilla es instanciada con el mismo tipo en muchas unidades de traducción, ésta puede aumentar dramáticamente el tiempo de compilación. No hay manera de prevenir esto en C++03, así que C++11 introdujo las declaraciones de plantilla extern, análogas a las declaraciones de datos extern.

C++03 tiene esta sintaxis para obligar al compilador a instanciar una plantilla:

template class std::vector<MiClase>;

C++11 ahora proporciona este sintaxis:

extern template class std::vector<MiClase>;

la cual le dice al compilador que no instancie la plantilla en esta unidad de traducción.

Mejoras en la usabilidad del núcleo del lenguaje

[editar]

Estas características existen para el propósito primario de hacer el lenguaje más fácil usar. Estas pueden mejorar la seguridad de tipo, minimizar la repetición de código, hacer que el código erróneo sea menos probable, etc.

  • Inicializador de listas
  • Inicialización uniforme
  • Inferencia de tipos
  • Bucle for basado en rango
  • Funciones y expresiones lambda
  • Sintaxis alternativa de función
  • Mejora en la construcción de objetos
  • Override y final explícitos
  • Constante de puntero nulo
  • Enumeraciones fuertemente tipeadas
  • Paréntesis de ángulo del lado derecho (Right angle bracket)
  • Operadores de conversión explícita
  • Alias de plantillas
  • Uniones sin restricciones
  • Identificadores con significado especial

Inicializador de listas

[editar]

C++03 heredó la característica del inicializador de lista de C. Un struct o un arreglo recibe una lista de argumentos en llaves cuadradas (corchetes), en el orden de las definiciones de los miembros en el struct. Estos inicializadores de listas son recursivos, así que pueden usarlos un arreglo de structs o un struct que contiene otros structs.

struct Objeto
{
    float primero;
    int segundo;
};

Objeto escalar = {0.43f, 10}; // Un objeto, con primero=0.43f y segundo=10
Objeto unArreglo[] = {{13.4f, 3}, {43.28f, 29}, {5.934f, 17}}; // Un arreglo con tres objetos

Esto es muy útil para las listas estáticas o justo para inicializar un struct con un valor particular. C++ también proporciona constructores para inicializar un objeto, pero a menudo no son tan convenientes como el inicializador de lista. Sin embargo C++03 solo permite inicializar listas en los structs y las clases que se ajustan a la definición Plain Old Data (POD); C++11 extiende la inicialización de listas, así que pueden ser usadas para todas las clases incluyendo los contenedores estándar como el std::vector.

C++11 ata (bind) el concepto a una plantilla, llamada std::initializer_list. Esto permite que los constructores y otras funciones tomen inicializadores de lista como parámetros. Por ejemplo:

class ClaseDeSecuencia {
public:
    ClaseDeSecuencia(std::initializer_list<int> list);
};

Esto permite que ClaseDeSecuencia sea construida de una secuencia de números enteros, como en:

ClaseDeSecuencia alguna_variable = {1, 4, 5, 6};

Esta es una clase especial de constructor, llamada constructor inicializador de lista. Las clases con tal constructor son tratadas especialmente durante la inicialización uniforme (ver abajo)

La clase std::initializer_list<> es un tipo de biblioteca estándar C++11 de primera clase. Sin embargo, solo pueden ser inicialmente construidos estáticamente por el compilador de C++11 con el uso de la sintaxis {}. La lista puede ser copiada una vez construida, por lo que es solamente una copia-por-referencia. Una lista de inicializador es constante; una vez que la lista del inicializador es creada, sus miembros no pueden ser cambiados, ni los datos en esos miembros.

Debido a que el initializer_list es un verdadero tipo, puede ser usado en otros lugares además de los constructores de clase. Las funciones regulares pueden tomar tipos de listas inicializadas como argumentos. Por ejemplo:

void nombre_de_funcion(std::initializer_list<float> list);

nombre_de_funcion({1.0f, -3.45f, -0.4f});

Los contenedores estándar también pueden ser inicializados de las maneras siguientes:

std::vector<std::string> v = { "xyzzy", "plugh", "abracadabra" };
std::vector<std::string> v{ "xyzzy", "plugh", "abracadabra" };

Inicialización uniforme

[editar]

El C++03 tiene un número de problemas con los tipos de inicialización. Hay varias maneras de inicializar tipos, y no todas producen los mismos resultados cuando son intercambiadas. La sintaxis tradicional del constructor, por ejemplo, puede parecerse como una declaración de función, y deben tomarse medidas para asegurarse que la regla del parse más irritante no la confundirá como tales. Solamente los agregados y los tipos POD pueden ser inicializados con inicializadores agregados (usando SomeType var = {/*stuff*/};).

C++11 proporciona una sintaxis que permite la inicialización de tipo completamente uniforme que trabaja en cualquier objeto. Se expande en la sintaxis de inicializador de lista:

struct EstructuraBasica {
    int x;
    double y;
};

struct EstructuraAlterna {
    EstructuraAlterna(int x, double y) : x_{x}, y_{y} {}
private:
    int x_;
    double y_;
};

EstructuraBasica var1{5, 3.2};
EstructuraAlterna var2{2, 4.3};

La inicialización de var1 se comporta exactamente como si fuera una inicialización de agregado. Es decir, cada miembro de datos de un objeto, a su vez, será inicializado por copia con el correspondiente valor de la lista de inicialización. La conversión de tipo implícita será usada cuando se necesite. Si no existe ninguna conversión, o solo existe un estrechamiento de conversión, el programa está malformado. La inicialización de var2 invoca al constructor.

También se puede hacer lo siguiente:

struct CadenaDeIdentificacion {
    std::string nombre;
    int identificador;
};

CadenaDeIdentificacion lee_cadena()
{
    return {"AlgúnNombre", 4}; // Note la falta de un tipo explícito
}

La inicialización uniforme no reemplaza la sintaxis de constructor. Todavía hay momentos en que es requerida la sintaxis de constructor. Si una clase tiene un constructor (TypeName(initializer_list<AlgunTipo>);), entonces toma prioridad sobre otras formas de construcción, a condición de que la lista del inicializador se conforme al tipo de constructor de secuencia. La versión C++11 del std::vector tiene un constructor de lista de inicialización para su tipo de plantilla. Esto significa que el siguiente código:

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

llamará al constructor de la lista del inicializador, no el constructor del std::vector que toma un solo parámetro de tamaño y crea el vector con ese tamaño. Para acceder al último constructor, el usuario necesitará usar directamente la sintaxis de constructor estándar.

Inferencia de tipo

[editar]

En el C++03 (y en C), el tipo de una variable debe ser explícitamente especificado para poder usarlo. Sin embargo, con el advenimiento de los tipos de plantilla y técnicas de metaprogramación de plantillas, puede que expresar el tipo de algo no sea fácil, particularmente cuando se trata de definir bien el valor de retorno de una función. También es difícil almacenar valores intermediarios en variables, y posiblemente se requerirá conocer las partes internas de una biblioteca de metaprogramación particular.

C++11 permite que esto sea mitigado de dos maneras. Primero, la definición de una variable con una inicialización explícita puede usar la palabra clave auto. Esto crea una variable del tipo específico del inicializador:

auto algun_extraño_tipo_llamable = boost::bind(&alguna_funcion, _2, _1, algun_objeto);
auto otra_variable = 5;

El tipo algun_extraño_tipo_llamable es simplemente lo que la plantilla particular de la función override de boost::bind retorna para esos argumentos particulares. Este tipo es fácilmente determinado por procedimiento por el compilador como parte de sus trabajos de análisis semántico, pero no es fácil de determinar por inspección por el usuario.

El tipo otra_variable también está bien definido, pero es más fácil de determinar por el usuario. Es un int, el cual es el mismo tipo que el literal de número entero.

Adicionalmente, la palabra clave decltype puede ser usada para determinar el tipo de una expresión en tiempo de compilación. Por ejemplo:

int algun_entero;
decltype(algun_entero) otra_variable_entera = 5;

Esto es más útil conjuntamente con auto, puesto que el tipo de una variable auto es solo conocido por el compilador. Sin embargo, el decltype también puede ser muy útil para las expresiones en el código que hacen un fuerte uso de sobrecarga de operador y de tipos especializados.

auto es también útil para reducir la verbosidad del código. Por ejemplo, en vez de escribir

for (std::vector<int>::const_iterator iterador = mi_vector.cbegin(); iterador != mi_vector.cend(); ++iterador)

el programador puede utilizar el más corto

for (auto iterador = mi_vector.cbegin(); iterador != mi_vector.cend(); ++iterador)

Esta diferencia crece a medida que el programador comienza a anidar los contenedores, aunque en tales casos los typedefs son una buena manera de disminuir la cantidad de código.

El tipo denotado por el decltype puede ser diferente del tipo deducido por auto.

#include <vector>
int main()
{
    const std::vector<int> v(1);
    auto a = v[0];        // a tiene el tipo int
    decltype(v[0]) b = 1; // b tiene el tipo const int&, el tipo de retorno de
                          // std::vector<int>::operator[](size_type) const
    auto c = 0;           // c tiene el tipo int
    auto d = c;           // d tiene el tipo int
    decltype(c) e;        // e tiene el tipo int, el tipo de la entidad llamada por c
    decltype((c)) f = c;  // f tiene el tipo int&, porque (c) es un lvalue
    decltype(0) g;        // g tiene el tipo int, porque 0 es un rvalue
}

Bucle for basado en rango

[editar]

En el C++03, la iteración sobre los elementos de una lista requiere mucho código. Otras lenguajes como C# y Java tienen atajos que permitan escribir una simple declaración "foreach" que camina automáticamente por la lista desde el comienzo hasta el final.

C++11 agregó una característica similar. La declaración for permite una iteración fácil sobre una lista de elementos:

int mi_arreglo[5] = {1, 2, 3, 4, 5};
for (int &x : mi_arreglo) {
    x *= 2;
}

Esta forma de for, llamada "for basado en rango", iterará sobre cada elemento en la lista. Trabajará para los arreglos de estilo C, inicializadores de listas, y cualquier tipo que tenga definida una función begin() y end() que retorne iteradores. Todos los contenedores de biblioteca estándar que tienen pares begin/end trabajarán con la sentencia for basada en rango.

Funciones lambda y expresiones

[editar]

C++11 proporciona la capacidad de crear funciones anónimas, llamadas funciones lambda. Estas se definen como sigue:

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

El tipo de retorno está implícito; devuelve el tipo de la expresión de retorno (decltype (x+y)). El tipo de retorno de una lambda puede ser omitido siempre y cuando todas las expresiones de retorno devuelven el mismo tipo.

Sintaxis alternativa de función

[editar]

la sintaxis de declaración de una función estándar de C era perfectamente adecuada para el conjunto de características de este lenguaje. A medida que C++ evolucionó desde el C, mantuvo la sintaxis básica extendiéndola en caso necesario. Sin embargo, a medida que C++ llegó a ser más complicado, expuso un número de limitaciones, particularmente con respecto a declaraciones de funciones de plantilla. Lo siguiente, por ejemplo, no es permitido en C++03:

template<class Lhs, class Rhs>
  Ret funcion_de_suma(const Lhs &lhs, const Rhs &rhs) {return lhs + rhs;} // Ret debe ser el tipo de lhs+rhs

El tipo Ret es cualquier cosa que la adición de los tipos Lhs y Rhs produzca. Incluso con la ya mencionada funcionalidad C++11 del decltype, esto no es posible:

template<class Lhs, class Rhs>
  decltype(lhs+rhs) funcion_de_suma(const Lhs &lhs, const Rhs &rhs) {return lhs + rhs;} // No es legal en C++11

Esto no es C++ legal porque lhs y rhs todavía no se han definido; no serán identificadores válidos hasta después de que el parser haya analizado el resto del prototipo de la función.

Para solucionarlo, C++11 introdujo una nueva sintaxis de declaración de función, con el tipo de retorno al final:

template<class Lhs, class Rhs>
  auto funcion_de_suma(const Lhs &lhs, const Rhs &rhs) -> decltype(lhs+rhs) {return lhs + rhs;}

Esta sintaxis puede ser usada para declaraciones y definiciones de funciones más mundanas:

struct AlgunaEstructura  {
    auto nombre_de_funcion(int x, int y) -> int;
};

auto AlgunaEstructura::nombre_de_funcion(int x, int y) -> int {
    return x + y;
}

En este caso el uso de la palabra clave "auto" significa algo diferente de su uso en la deducción de tipo automática.

Mejora en la construcción de objetos

[editar]

En C++03, a los constructores de una clase no se les permite llamar a otros constructores de esa clase; cada constructor debe construir todos sus miembros de clase por sí mismo o llamar a una función de miembro común, como los siguientes,

class AlgunTipo  {
    int numero;

public:
    AlgunTipo(int nuevo_numero) : numero(nuevo_numero) {}
    AlgunTipo() : numero(42) {}
};
class AlgunTipo  {
    int numero;

private:
    void Construct(int nuevo_number) { numero = nuevo_numero; }

public:
    AlgunTipo(int nuevo_numero) { Construct(nuevo_numero); }
    AlgunTipo() { Construct(42); }
};

Los constructores para las clases base no pueden ser expuestos directamente a las clases derivadas; cada clase derivada debe implementar constructores incluso si un constructor de clase base fuera apropiado. los miembros de datos no constantes de las clases no pueden ser inicializados en el sitio de la declaración de esos miembros. Solo pueden ser inicializados en un constructor.

El C++11 proporciona soluciones a todos estos problemas.

El C++11 permite a los constructores llamar a otros constructores pares (conocidos como delegación). Esto permite a los constructores usar el comportamiento de otro constructor con un mínimo de código añadido. Ejemplos de otros lenguajes similares a C++ que proporcionen la delegación son Java, C#, y D.

Esta sintaxis es como sigue:

class AlgunTipo  {
    int numero;

public:
    AlgunTipo(int nuevo_numero) : numero(nuevo_numero) {}
    AlgunTipo() : AlgunTipo(42) {}
};

Note que, en este caso, el mismo efecto habría podido ser alcanzado haciendo new_number un parámetro por defecto. Sin embargo, la nueva sintaxis permite que al valor por defecto (42) sea expresado en la implementación en vez de la interfaz - un beneficio para los mantenedores del código de la biblioteca puesto que los valores por defecto para los parámetros de la función son "horneados" para llamar sitios, mientras que la delegación del constructor permite que el valor sea cambiado usando la biblioteca sin la recompilación del código.

Esto viene con una advertencia: C++03 considera un objeto a ser construido cuando su constructor termine de ejecutarse, pero C++11 considera un objeto construido una vez que cualquier constructor termine su ejecución. Puesto que se permitirá ejecutar a múltiples constructores, esto significará que cada constructor delegado ejecutará en un objeto completamente construido de su propio tipo. Los constructores de clase derivados, ejecutarán después de que toda la delegación en sus clases base esté completa.

Para los constructores de la clase base, el C++11 permite a una clase especificar que los constructores de la clase base sean heredados. Esto significa que el compilador C++11 generará código para realizar la herencia, el forwarding de clase derivada a la clase base. Observe que esto es una característica de todo o nada; todos los constructores de esa clase base son forwarded o ningunos de ellos lo son. También, observe que hay restricciones para la herencia múltiple, de tal manera que los constructores de clase no pueden ser heredados a partir de dos clases que usen constructores con la misma firma. Ni puede existir un constructor en la clase derivada que tenga una firma igual en la clase base heredada.

la sintaxis es como sigue:

class ClaseBase {

public:
    ClaseBase(int valor);
};

class ClaseDerivada : public ClaseBase {

public:
    using ClaseBase::ClaseBase;
};

Para la inicialización de miembros, C++11 permite la siguiente sintaxis:

class AlgunaClase {

public:
    AlgunaClase() {}
    explicit AlgunaClase(int nuevo_valor) : valor(nuevo_valor) {}

private:
    int valor = 5;
};

Cualquier constructor de la clase inicializará el valor en 5, si el constructor no sobreescribe la inicialización con las propios. Así que el constructor vacío de arriba inicializará el valor como establece la definición de la clase, pero el constructor que toma un int, lo inicializará con el parámetro dado.

También puede usar la inicialización de constructor o uniforme, en vez de la inicialización de igualdad mostrada arriba.

Override y final explícitos

[editar]

En C++03, es posible crear accidentalmente una nueva función virtual, cuando en realidad uno solo se prepuso un override de una función de la clase base. Por ejemplo:

struct Base {
    virtual void alguna_funcion(float);
};

struct Derivada : Base {
    virtual void alguna_funcion(int);
};

Por ejemplo, queremos diseñar Derivada::alguna_funcion para reemplazar la versión de la clase base. Pero, debido a que la función virtual tiene una firma diferente, en realidad crea una segunda función virtual. Esto es un problema común, particularmente cuando un usuario va a modificar la clase base.

C++11 proporciona una sintaxis para solucionar este problema.

struct Base {
    virtual void alguna_funcion(float);
};

struct Derivada : Base {
    virtual void alguna_funcion(int) override; // Mal formado porque no hace un override de un método de la clase base
};

El identificador especial override significa que el compilador comprobará la clase base para ver si hay una función virtual con esta firma exacta. Y si no la hay, el compilador producirá un error.

C++11 también agrega la capacidad de prevenir poder heredar clases o sobrecargar funciones específicas. Esto se consigue con el identificador especial final. Por ejemplo:

struct Base1 final { };

struct Derivada1 : Base1 { }; // Mal formado porque la clase base está marcada como final

struct Base2 {
    virtual void f() final;
};

struct Derivada2 : Base2 {
    void f(); // Mal formado porque la función virtual Base2::f está marcada como final
};

En este ejemplo, la sentencia virtual void f() final; declara una nueva función virtual, pero también previene a las clases derivadas poder sobrecargarla. También tiene el efecto de prevenir, a las clases derivadas, de usar esa combinación particular de nombre de función y parámetros.

Observe que ni override ni final son palabras claves del lenguaje. Técnicamente son identificadores; solo ganan un significado especial cuando son usados en esos contextos específicos. En cualquier otra localización, pueden ser identificadores válidos.

Constante de puntero null

[editar]

Únicamente para los propósitos de esta sección, cada ocurrencia de "0" significará "expresión constante que se evalúa a 0, que es del tipo entero int". En realidad, la expresión constante puede ser de cualquier tipo entero.

Desde el amanecer de C en 1972, la constante 0 ha tenido el doble papel de constante de número entero y de constante de puntero nulo. La ambigüedad inherente en el doble significado de 0 fue tratada en C por el uso del macro NULL del preprocesador, que comúnmente expande a ((void*)0) ó 0. C++ no adoptó el mismo comportamiento, permitiendo a 0 solamente como constante del puntero nulo. Esto interactúa mal con la sobrecarga de funciones:

void foo(char *);
void foo(int);

Si NULL está definido como 0 (lo usual en el caso del C++), la sentencia foo(NULL); llamará a foo(int), que casi con toda certeza no es lo que pensó el programador, y no es lo que sugiere una lectura superficial del código.

C++11 corrige esto introduciendo una nueva palabra clave para servir como constante inconfundible de puntero nulo: nullptr. Es del tipo nullptr_t, que es implícitamente convertible y comparable a cualquier tipo de puntero o tipo de puntero-a-miembro. No es implícitamente convertible o comparable a los tipos enteros, a excepción de bool. Mientras que la propuesta original especificó que un rvalue del tipo nullptr no debería ser convertible a bool, el grupo de trabajo del núcleo del lenguaje decidió que tal conversión sería deseable, para la consistencia con los tipos punteros regulares. Los cambios de redacción propuestos fueron votados unánimemente en el documento de trabajo en junio de 2008.[1]

Por razones de compatibilidad hacia atrás, 0 sigue siendo un constante de puntero nulo válida.

char *pc = nullptr;     // OK
int  *pi = nullptr;     // OK
bool   b = nullptr;     // OK. b es false.
int    i = nullptr;     // error
foo(nullptr);           // invoca foo(char *), no foo(int);

Enumeraciones de tipo estricto

[editar]

En C++03, las enumeraciones no son de tipo seguro. Internamente, en realidad son números enteros, incluso cuando los tipos de la enumeración son distintos. Esto permite la comparación entre dos valores de diferentes tipos de enumeración. La única seguridad que C++03 proporciona es que un número entero o un valor de un tipo enum no se convierte implícitamente a otro tipo enum. Además, el tipo 'int' subyacente está definido por la implementación del compilador. Por lo tanto, el código que depende del tamaño de la enumeración no es portable. Por último, los valores de la enumeración tienen un ámbito definido por el ámbito que los encierra. Así, no es posible que dos enumeraciones separadas tengan nombres de miembro que emparejen.

C++11 permitirá una clasificación especial de la enumeración que no tiene ninguno de esos problemas. Esto es expresado usando la declaración enum class (enum struct también es aceptado como un sinónimo):

enum class Enumeracion {
    Valor1,
    Valor2,
    Valor3 = 100,
    Valor4 /* = 101 */
};

Esta enumeración es de tipo seguro; los valores de enum class no son convertidos implícitamente a números enteros. Por lo tanto, no pueden ser comparados con números enteros (la expresión Enumeracion::Valor4 == 101 da un error del compilador).

El tipo subyacente de los miembros de las clases de enum no estará predefinido. Por defecto, como en el caso anterior, es int, pero puede ser especificado explícitamente a un tipo diferente:

enum class Enumeracion2 : unsigned int {Valor1, Valor2};

El ámbito de la enumeración también está definido como el ámbito del nombre de la enumeración. Usando los nombres de enumerador requiere explícitamente el uso de ámbitos. Valor1 es indefinido, pero Enumeracion2::Valor1 está definido.

Adicionalmente, C++11 permitirá que las enumeraciones estándar proporcionen un ámbito explícito así como la definición del tipo subyacente:

enum Enumeracion3 : unsigned long {Valor1 = 1, Valor2};

Los nombres del enumerador son definidos en el ámbito de la enumeración (Enumeración3::Valor1), pero para la compatibilidad hacia atrás, los nombres del enumerador también son colocados en el ámbito de encerrado.

La declaración de enums también es posible en C++11. Previamente, los tipos de enum no podían ser declarados porque el tamaño de la enumeración depende de la definición de sus miembros. Siempre que el tamaño de la enumeración sea especificado implícita o explícitamente, puede ser declarado lo siguiente:

enum Enumeracion1;                      // Ilegal en C++ y C++11; el tipo subyacente no puede ser determinado
enum Enumeracion2 : unsigned int;       // Legal en C++11, el tipo subyacente está especificado explícitamente
enum class Enumeracion3;                // Legal en C++11, el tipo subyacente es int
enum class Enumeracion4 : unsigned int; // Legal en C++11.
enum Enumeracion2 : unsigned short;     // Ilegal en C++11, porque Enumeracion2 estaba declarado con un tipo subyacente diferente.

Paréntesis de ángulo del lado derecho

[editar]

El parser del C++03 define “>>” como el operador de desplazamiento derecho en todos los casos. Sin embargo, con declaraciones jerarquizadas de plantilla, hay una tendencia para que el programador descuide poner un espacio entre los dos paréntesis de ángulo derecho, causando por lo tanto un error de sintaxis del compilador.

C++11 mejorará la especificación del parser de modo que múltiples paréntesis de ángulo derechos sean interpretados, donde sea razonable, como cierre de la lista de argumentos de la plantilla. Esto puede ser sobreescrito usando paréntesis:

template<bool Prueba> class AlgunTipo;
std::vector<AlgunTipo<1>2>> x1;    // Interpretado como un std::vector de AlgunTipo<true> 2>,
// lo cual NO es una sintaxis legal. 1 es true.
std::vector<AlgunTipo<(1>2)>> x1;  // Interpretado como un std::vector de AlgunTipo<false>,
// Lo cual SÍ es una sintaxis legal en C++11. (1>2) es false.

Operadores de conversión explícitos

[editar]

C++03 agregó la palabra clave explicit como un modificador para prevenir que los constructores con un solo argumento fueran usados como operadores de conversión de tipo implícito. Sin embargo, esto no hace nada por los reales operadores de conversión. Por ejemplo, una clase puntero inteligente puede tener un operator bool() para permitir que actúe más como un puntero primitivo: si incluye esta conversión, puede ser probada con if(smart_ptr_variable) (el cual sería true si el puntero no fuera null y de lo contrario false). Sin embargo, esto también permite otras conversiones no intencionales. Puesto que el bool de C++ es definido como tipo aritmético, puede ser implícitamente convertido a los tipos entero o aún a los tipos flotantes, lo que permite las operaciones matemáticas que no fueron pensadas por el usuario.

En C++11, la palabra clave explicit ahora puede ser aplicada a los operadores de conversión. Como con los constructores, previene el uso de funciones de conversión en conversiones implícitas. Sin embargo, contextos del lenguaje que específicamente requieren valor boleano, (las condiciones de sentencias if y en bucles, tan bien como operandos en operadores lógicos), como en conversiones explícitas y por lo tanto pueden usar a un operador de conversión bool.

Alias de plantillas

[editar]

En C++03, solo es posible definir un typedef como un sinónimo para otro tipo, incluyendo un sinónimo para una especialización de plantilla con todos los argumentos reales de la plantilla especificados. No es posible crear una plantilla de typedef como la siguiente:

template <typename Primero, typename Segundo, int tercero>
class AlgunTipo;
template <typename Segundo>
typedef AlgunTipo<OtroTipo, Segundo, 5> TypedefName; // Ilegal en C++

Esto no compilará.

C++11 agregará esta capacidad con la siguiente sintaxis:

template <typename Primero, typename Segundo, int tercero>
class AlgunTipo;

template <typename Segundo>
using TypedefName = AlgunTipo<OtroTipo, Segundo, 5>;

En C++11, la sintaxis también puede ser usada para alias de tipos:

typedef void (*Tipo)(double);		// Viejo estilo
using Tipo = void (*)(double);	// Nueva sintaxis introducida

Uniones sin restricción

[editar]

En C++03 hay restricciones en qué tipos de objetos pueden ser miembros de una unión. Por ejemplo, las uniones no pueden contener objetos que definan a un constructor no trivial. C++11 suprime algunas de estas restricciones.[2]

Éste es un simple ejemplo de una unión permitida en C++:

//for placement new
#include <new>

struct Punto  {
    Punto() {}
    Punto(int x, int y): x_(x), y_(y) {}
    int x_, y_;
};

union U {
    int z;
    double w;
    Punto p;                   // Ilegal en C++; Punto tiene un constructor no trivial.  Sin embargo, esto es legal en C++11.
    U() { new( &p ) Punto(); } // Funciones miembro no triviales son definidas implícitamente por una unión;
                               // Si se requiere, en su lugar, ellas son borradas para forzar una definición manual.
};

Los cambios no romperán ningún código existente puesto que solamente se relajan las reglas actuales.

Identificadores con significado especial

[editar]

Los identificadores override y final tienen un significado especial solo cuando son usados en un cierto contexto, de lo contrario pueden usarse como identificadores normales.

Mejoras en la funcionalidad del núcleo del lenguaje

[editar]

Estas características permiten al lenguaje hacer cosas que eran previamente imposibles, excesivamente verbosas, o requerían bibliotecas no portables.

  • Plantillas variadic
  • Nuevos literales de string
  • Literales definidos por el usuario
  • Modelo de memoria multitarea
  • Almacenamiento local de hilos
  • Miembros de funciones especiales defaulted y deleted explícitos
  • Tipo long long int
  • Aserciones estáticas
  • Permitir a sizeof trabajar en miembros de clase sin un objeto explícito
  • Permitir implementaciones de recolección de basura

Plantillas de Variadic

[editar]

En C++11, las plantillas pueden tomar números variables de parámetros. Esto también permite la definición de funciones variadic tipo seguro.

Nuevos literales de string

[editar]

C++03 ofrece dos clases de literales de string (cadena). La primera clase, contenida dentro de comillas dobles, produce un arreglo terminado en null de tipo const char. La segunda clase, definida como L"", produce un arreglo terminado en null de tipo const wchar_t, donde wchar_t es un carácter ancho. Ninguno de los tipos literales ofrece soporte para literales de string con UTF-8, UTF-16, o cualquier otra clase de codificaciones de Unicode.

Con el propósito de realzar en los compiladores de C++ el soporte de unicode, la definición del tipo char ha sido modificada para que tenga por lo menos el tamaño necesario para almacenar una codificación UTF-8 de ocho bits y lo suficientemente grande como para contener cualquier miembro del juego de caracteres de ejecución básicos del compilador. Anteriormente estaba definida solo como esto último.

Además de este cambio en la definición de char, C++11 soportará tres codificaciones de Unicode: UTF-8, UTF-16, y UTF-32, y añadirá dos nuevos tipos de carácter: char16_t y char32_t. Estos están diseñados para almacenar UTF-16 y UTF-32 respectivamente.

El ejemplo siguiente muestra cómo crear literales string para cada una de estas codificaciones:

u8"I'm a UTF-8 string."
u"This is a UTF-16 string."
U"This is a UTF-32 string."

El tipo del primer string es el habitual const char[]. El tipo del segundo string es const char16_t[] y el tipo del tercero es const char32_t[].

Al construir literales de string unicode, a menudo es útil insertar códigos de unicode directamente. Para ello, C++11 permitirá la siguiente sintaxis:

u8"This is a Unicode Character: \u2018."
u"This is a bigger Unicode Character: \u2018."
U"This is a Unicode Character: \u2018."

El número después del \u es un número hexadecimal; sin necesidad del prefijo 0x. El identificador \u representa un código de unicode de 16 bits; para insertar un código de 32 bits, se usa \U y un número hexadecimal de 32 bits. Solamente se admiten códigos válidos de Unicode. Por ejemplo, los valores en el intervalo U+D800-U+DFFF están prohibidos, pues están reservados para los pares sustitutos en las codificaciones UTF-16.

A veces también es útil evitar secuencias de escape de string manuales, particularmente al usar literales de archivos XML, lenguajes de scripting, o de expresiones regulares. C++11 proporcionará un literal de cadena raw:

R"(The String Data \ Stuff " )"
R"delimiter(The String Data \ Stuff " )delimiter"

En el primer caso, todo entre "( y )" es parte del string. Los caracteres " y \ no necesitan secuencia de escape. En el segundo caso, el "delimiter( inicia la cadena, y solamente se termina al encontrar )delimiter". El delimiter puede ser cualquier literal de hasta 16 caracteres de longitud. Este no puede contener espacios, caracteres de control, o los caracteres "(", ")", o "\". El uso de este delimitador permite que el usuario tenga caracteres ")" dentro de literales de cadena raw. Por ejemplo, R"delimiter((a-z))delimiter" es equivalente a "(a-z)".[3]

Los literales de cadena raw pueden combinarse con los prefijos de literal ancho o con cualquiera de los prefijos de literal unicode:

u8R"XXX(I'm a "raw UTF-8" string.)XXX"
uR"*(This is a "raw UTF-16" string.)*"
UR"(This is a "raw UTF-32" string.)"

Literales definidos por el usuario

[editar]

C++03 proporciona un número de literales. Los caracteres "12.5" son un literal que es resuelto por el compilador como tipo double con el valor de 12.5. Sin embargo, la adición del sufijo "f", como en "12.5f", crea un valor del tipo float que contiene el valor 12.5. Los modificadores de sufijo para los literales son fijados por la especificación del C++, y el código de C++ no puede crear nuevos modificadores literales.

C++11 también incluirá la capacidad para que el usuario defina nuevas clases de modificantes literales que construyan objetos basados en string de caracteres que el literal modifique.

La transformación de los literales es redefinida en dos fases distintas: raw y cooked (crudo y cocinado). Un literal raw es una secuencia de caracteres de un cierto tipo específico, mientras que el literal cocinado es de un tipo separado. El literal 1234 de C++, como literal crudo, es esta secuencia de caracteres: '1', '2', '3', '4'. Como literal cocinado, es el número entero 1234. El literal 0xA de C++ en forma cruda es '0', 'x', 'A', mientras que en forma cocinada es el número entero 10.

Los literales pueden ser extendidos tanto en formas crudas y cocinadas, con la excepción de los literales de string, que solo se pueden procesar en forma cocinada. Esta excepción es debido al hecho de que los string tienen prefijos que afecten al significado y al tipo específicos de los caracteres en cuestión.

Todos los literales definidos por el usuario son sufijos; la definición de literales de prefijo no es posible.

Los literales definidos por el usuario que procesan la forma cruda del literal son definidos como sigue:

OutputType operator "" _suffix(const char *literal_string);

OutputType some_variable = 1234_suffix;

La segunda declaración ejecuta el código definido por la función literal definida por el usuario. A esta función se le pasa "1234" como un string de estilo C, así que tiene un terminador null.

Un mecanismo alternativo para procesar literales de número entero y punto flotante crudos es a través de una plantilla variadic:

template<char...> OutputType operator "" _suffix();

OutputType some_variable = 1234_suffix;
OutputType another_variable = 2.17_suffix;

Esto instancia la función de procesamiento literal como operator "" _suffix<'1', '2', '3', '4'>(). En esta forma, no hay carácter null terminal en el string. El propósito principal para hacer esto es utilizar palabra clave constexpr de C++11 y permitir al compilador que el literal sea transformado enteramente en tiempo de compilación, asumiendo que OutputType es un tipo constexpr construible y copiable, y la función de procesamiento literal es una función constexpr.

Para los literales numéricos, el tipo de literal cocinado es unsigned long long para literales enteros, o long double para los literales de punto flotante. (Nota: No hay necesidad de tipos entero con signo porque un literal con prefijo de signo es parseado como expresión que contiene el signo como operador de prefijo unario seguido del número sin signo). No hay forma de plantilla alternativa:

OutputType operator "" _suffix(unsigned long long);
OutputType operator "" _suffix(long double);

OutputType some_variable    = 1234_suffix;   // usa la primera función
OutputType another_variable = 3.1416_suffix; // usa la segunda función

Para los literales de string, de acuerdo con los prefijos de string previamente mencionados, son usados:

OutputType operator "" _suffix(const char * string_values, size_t num_chars);
OutputType operator "" _suffix(const wchar_t * string_values, size_t num_chars);
OutputType operator "" _suffix(const char16_t * string_values, size_t num_chars);
OutputType operator "" _suffix(const char32_t * string_values, size_t num_chars);

OutputType some_variable = "1234"_suffix;      // Llama la versión de la const char *
OutputType some_variable = u8"1234"_suffix;    // Llama la versión de la const char *
OutputType some_variable = L"1234"_suffix;     // Llama la versión de la const wchar_t *
OutputType some_variable = u"1234"_suffix;     // Llama la versión de la const char16_t *
OutputType some_variable = U"1234"_suffix;     // Llama la versión de la const char32_t *

No hay forma alternativa de plantilla. Los literales de carácter son definidos similarmente.

Modelo de memoria de multitarea

[editar]

El comité estándar de C++ planea estandardizar el soporte para la programación multihilo.

Hay dos partes implicadas: La primera parte es la definición de un modelo de memoria que permitirá que múltiples hilos coexistan en un programa, y la definición del soporte para la interacción entre los hilos. La segunda parte será proporcionada vía facilidades de la biblioteca. (Ver la sección de este artículo C++11#Facilidades de hilos).

El modelo de memoria define cuando los múltiples hilos pueden tener acceso a la misma posición de memoria, y especifica cuando las actualizaciones por un hilo llegan a ser visibles a otros hilos.

Almacenamiento de hilo local

[editar]

En un ambiente con multi hilo, es común que cada hilo tenga algunas variables únicas. Esto actualmente sucede para las variables locales de una función, pero no sucede para las variables globales y estáticas.

Para el estándar ha sido propuesto una nueva duración de almacenamiento de hilo local (thread-local), además de las existentes estático, dinámico y automático, (static, dynamic, automatic). El almacenamiento de hilo local será indicado por el especificador de almacenamiento thread_local.

Cualquier objeto que pudiera tener la duración de almacenamiento estático (es decir, perdurar durante toda la ejecución del programa) se le puede dar la duración de hilo local. Lo que se quiere es que como cualquier otra variable de duración estática, un objeto hilo local pueda ser inicializado usando un constructor y destruido usando un destructor.

Miembros de funciones especiales defaulted y deleted explícitos

[editar]

En C++03, el compilador proporciona, para las clases que no lo proporcionan por sí mismas, un constructor por defecto (default), un constructor de copia, un operador de asignación de copia (operator=), y un destructor. El programador puede sobreescribir estos comportamientos por defecto definiendo versiones personalizadas. C++ también define a varios operadores globales (tales como operator= y operator new) que trabajan sobre todas las clases, y que el programador puede sobreescribir.

Sin embargo, hay poco control sobre la creación de estos comportamientos por defecto. Hacer una clase intrínsecamente no copiable, por ejemplo, requiere la declaración de un constructor de copia y de un operador de asignación privados y la no definición de ellos. El intentar usar estas funciones es una violación de la regla de una sola definición. Como no se requiere un mensaje de diagnóstico,[4]​ esto da lugar típicamente a un error del enlazador (linker).[cita requerida]

En el caso del constructor default, el compilador no generará un constructor por defecto si una clase es definida con cualquier constructor. Esto es útil en muchos casos, pero es también útil para poder tener constructores especializados y el de defecto generado por el compilador.

C++11 permitirá el default y delete explícito de estas funciones miembro especiales. Por ejemplo, el tipo siguiente declara explícitamente que está usando al constructor default:

struct SomeType {
    SomeType() = default; // Declara explícitamente el constructor por defecto.
    SomeType(OtherType value);
};

Alternativamente, ciertas características pueden ser inhabilitadas explícitamente. Por ejemplo, el tipo siguiente es no copiable:

struct NonCopyable {
    NonCopyable & operator=(const NonCopyable&) = delete;
    NonCopyable(const NonCopyable&) = delete;
    NonCopyable() = default;
};

El especificador = delete puede ser usado para prohibir llamar cualquier función, lo cual puede ser usado para rechazar la llamada de una función de miembro con parámetros particulares. Por ejemplo:

struct NoInt {
    void f(double i);
    void f(int) = delete;
};

Un intento de llamar a f() con un int será rechazado por el compilador, en vez de realizar una conversión silenciosa a double. Esto puede ser generalizado para rechazar la llamada de la función con cualquier tipo con excepción de doble, de la siguiente manera:

struct OnlyDouble {
    void f(double d);
    template<class T> void f(T) = delete;
};

Tipo long long int

[editar]

En C++03, el más grande tipo entero es long int. Está garantizado que tenga por lo menos tantos bits usables como int. Esto resultaba en que long int tuviera un tamaño de 64 bits en algunas implementaciones populares y 32 bits en otras. Para abordar este problema, C++11 agrega un nuevo tipo entero long long int. Está garantizado que sea por lo menos tan grande como un long int, y que tenga no menos de 64 bits. El tipo fue originalmente introducido al C estándar con C99, y la mayoría de los compiladores de C++ ya lo soportan como extensión.[5][6]

Aserciones estáticas

[editar]

C++03 proporciona dos métodos para probar aserciones: el macro assert y la directiva de preprocesador #error. Sin embargo, ninguno es apropiado para el uso en plantillas: el macro prueba la aserción en tiempo de ejecución, mientras que la directiva del preprocesador prueba la aserción durante el preprocesamiento, que sucede antes de la instanciación de las plantillas. Ninguno es apropiado para probar las propiedades que son dependientes de parámetros de plantillas.

La nueva utilidad introduce una nueva manera de probar aserciones en tiempo de compilación, usando la nueva palabra clave static_assert. La declaración asume la siguiente forma:

static_assert (constant-expression, error-message);

Aquí están algunos ejemplos de cómo puede ser usado el static_assert:

static_assert ((GREEKPI > 3.14) && (GREEKPI < 3.15), "GREEKPI is inaccurate!");
template<class T>
struct Check  {
    static_assert (sizeof(int) <= sizeof(T), "T is not big enough!");
};

Cuando la expresión constante es false el compilador produce un mensaje de error. El primer ejemplo representa una alternativa a la directiva de preprocesador #error, en contraste, en el segundo ejemplo la aserción es chequeada en cada instanciación de la clase de plantilla Check.

Las aserciones estáticas también son útiles fuera de las plantillas. Por ejemplo, una implementación de un algoritmo pudiera depender de que el tamaño de un long long fuera más grande que un int, algo que el estándar no garantiza. Tal asunción es válida en la mayoría de los sistemas y de los compiladores, pero no todos.

Permite a sizeof trabajar en miembros de clases sin un objeto explícito

[editar]

En C++03, el operador sizeof puede ser usado en tipos y objetos. Pero no puede ser usado para hacer lo siguiente:

struct SomeType { OtherType member; };

sizeof(SomeType::member); // No trabaja en C++03. Válido con C++11

Esto permite en C++11 retornar el tamaño de OtherType. C++03 no permite esto, así que es un error de compilación.

Permite implementaciones con recolección de basura

[editar]

La implementación define si los objetos dinámicamente asignados que se vuelvan inalcanzables son reclamados automáticamente.

Cambios en la biblioteca estándar de C++

[editar]

Un número de nuevas características serán introducidas en la biblioteca estándar del C++11. Muchos de estas características pueden ser implementadas bajo el estándar actual, pero algunas dependen, en mayor o menor medida, en nuevas características del núcleo de C++11.

Una gran parte de las nuevas bibliotecas son definidas en el C++ Technical Report 1 (Reporte Técnico 1 de C++) (llamado TR1), publicado en 2005. Están disponibles actualmente varias implementaciones completas y parciales del TR1 usando el namespace std::tr1. Para C++11 serán movidas al namespace std. Sin embargo, a medida que las características del TR1 son llevadas a la biblioteca estándar de C++11, cuando sea apropiado, éstas son actualizadas con características del lenguaje de C++ que no estaban disponibles en la versión inicial del TR1. También, pueden ser mejoradas con las características que eran posibles debajo el C++03, pero no eran parte de la especificación original del TR1.

El comité se prepone crear un segundo reporte técnico (llamado TR2) después de que esté completa la estandardización de C++11. Las propuestas de biblioteca que no estén listas para C++11 serán puestas en el TR2 u otros reportes técnicos.

Las siguientes propuestas están en curso para el C++11.

  • Actualizaciones a los componentes de la biblioteca estándar
  • Facilidades de hilos
  • Tipos tuple
  • Tablas hash
  • Expresiones regulares
  • Apuntadores inteligentes de propósito general
  • Facilidad de número aleatorio extensible
  • Referencia de envoltorio (wrapper)
  • Envoltorios polimórficos para objetos de funciones
  • Características de tipo para metaprogramación
  • Método uniforme para computar el tipo de retorno de objetos de funciones

Actualizaciones a los componentes de la biblioteca estándar

[editar]

C++11 ofrece un número de nuevas características de lenguaje de las cuales se pueden beneficiar los actuales componentes de la biblioteca estándar. Por ejemplo, la mayoría de los containers (contenedores) de la biblioteca estándar pueden beneficiarse en la referencia Rvalue basada en el soporte del move constructor, tanto para mover rápidamente contenedores pesados alrededor, como para mover el contenido de esos contenedores a nuevas localizaciones de memoria. Los componentes de la biblioteca estándar serán actualizados con las nuevas características de lenguaje C++11 cuando sea apropiado. Estas incluyen, pero no se limitan necesariamente a:

  • Referencias Rvalue y el asociado soporte para move
  • Soporte para los tipos de caracteres Unicode de las unidades de codificación UTF-16, y UTF-32
  • Plantillas variadic (acopladas con las referencias de Rvalue para permitir forwarding perfecto)
  • Expresiones constantes en tiempo de compilación
  • Decltype
  • Operadores de conversión explícitos
  • Funciones Default/Deleted

Adicionalmente, ha pasado mucho tiempo desde que C++ fue estandardizado. Se ha escrito una gran cantidad de código usando la biblioteca estándar; esto ha revelado porciones de las bibliotecas estándar que podrían usar alguna mejora. Entre las muchas áreas de mejoras que son consideradas son los allocators de la biblioteca estándar. Un nuevo modelo basado en ámbito (scope) de allocators será incluido en el C++11 para complementar el modelo actual.

Facilidades de hilos

[editar]

Mientras que el lenguaje C++11 proporcionará un modelo de memoria que soporta hilos (threading), el soporte primario para usar realmente hilos vendrá con la biblioteca estándar del C++11.

Será proporcionada una clase de tipo hilo (std::thread). Para correr el nuevo hilo, esta clase tomará un objeto función y opcionalmente una serie de argumentos. Será posible hacer que un hilo se detenga hasta que otro hilo en ejecución termine, proporcionando soporte de thread joining a través de la función de miembro std::thread::join(). De ser posible, también será proporcionado el acceso a los objetos nativos de hilo subyacentes para las operaciones específicas de la plataforma, por medio de la función de miembro std::thread::native_handle().

Para la sincronización entre los hilos, serán agregadas a la biblioteca mutexes apropiados (std::mutex, std::recursive_mutex, etc) y variables de condición (std::condition_variable y std::condition_variable_any). Esto será accesible con locks RAII (std::lock_guard y std::unique_lock) y algoritmos de bloqueo (locking) para fácil uso.

Para el trabajo de bajo nivel de alto rendimiento, a veces es necesario la comunicación entre los hilos sin el overhead de los mutexes. Esto es alcanzado usando operaciones atómicas en localizaciones de memoria. Estas pueden opcionalmente especificar las restricciones de visibilidad de memoria mínimas requeridas para una operación. Las barreras de memoria explícitas también pueden ser usadas para este propósito.

La biblioteca de hilo del C++11 también incluirá futuros y promesas para pasar resultados asincrónicos entre los hilos, y el std::packaged_task para envolver (wrapping) una llamada de función que puede generar tal resultado asincrónico. La propuesta de futuros fue criticada porque carece de una manera de combinar futuros y comprobar la terminación de un interior de una promesa dentro de un conjunto de promesas.[7]

Otras facilidades de hilo de alto nivel tales como thread pools han sido remitidas a un futuro informe técnico del C++. No serán una parte de C++11, pero su eventual implementación es esperada a ser construida enteramente en el tope de las características de la biblioteca de hilos.

La nueva facilidad std::async facility proporciona un método conveniente de correr tareas y de atarlas a un std::future. El usuario puede elegir si la tarea debe correr asincrónicamente en un hilo separado, o sincrónicamente en un hilo que espere por el valor. Por defecto la implementación puede elegir, lo que proporciona una manera fácil de tomar ventaja de la concurrencia del hardware sin la sobresubscripción (oversubscription), y proporciona algunas de las ventajas de un thread pool para los usos simples.

Tipos tuple

[editar]

Los tuples son colecciones compuestas por los objetos heterogéneos de dimensiones pre-arregladas. Un tuple puede ser considerado una generalización de un miembro de estructura de las variables.

La versión C++11 del tuple del TR1 se beneficiará de las características C++11 como plantillas variadic. La versión TR1 requería un máximo número de tipos de contenedor definido por la implementación, y requería una sustancial cantidad de trucos de macro para implementarse razonablemente. Por contraste, la implementación de la versión C++11 no requiere ningún número máximo de tipos definidos explícitamente por la implementación. Aunque los compiladores casi ciertamente tendrán una profundidad interna máxima de recursión para la instanciación de plantillas (lo cual es normal), la versión de tuples de C++11 no expondrá este valor al usuario.

Usando plantillas variadic, la declaración de clase tuple se ve como sigue:

template <class ...Types> class tuple;

Un ejemplo de la definición y uso del tipo tuple:

typedef std::tuple <int, double, long &, const char *> test_tuple;
long lengthy = 12;
test_tuple proof (18, 6.5, lengthy, "Ciao!");

lengthy = std::get<0>(proof);  // Asigna a 'lengthy' el valor 18.
std::get<3>(proof) = " Beautiful!";  // Modifica el cuarto elemento del tuple.

Es posible crear el tuple proof sin la definición de su contenido, pero solamente si los tipos de elementos del tuple poseen constructores por defecto. Por otra parte, es posible asignar un tuple a otro tuple: si los tipos de los dos tuples son iguales, es necesario que cada tipo de elemento posea un constructor copy; de lo contrario, es necesario que cada tipo de elemento del tuple del lado derecho sea convertible al elemento correspondiente del tuple del lado izquierdo, o que el tipo de elemento correspondiente del tuple del lado izquierdo tenga un constructor conveniente.

typedef std::tuple <int , double, string       > tuple_1 t1;
typedef std::tuple <char, short , const char * > tuple_2 t2 ('X', 2, "Hola!");
t1 = t2 ;  // Ok, los primeros dos elementos pueden ser convertidos,
           // El tercer elemento puede ser construido desde un 'const char *'.

Hay disponibles Los operadores relacionales (entre tuples con el mismo número de elementos), y hay disponibles dos expresiones para comprobar las características de un tuple (solamente durante la compilación):

  • std::tuple_size<T>::value retorna el número de elementos de el tuple T,
  • std::tuple_element<I, T>::type retorna el tipo del número de objeto I del tuple

Tablas hash

[editar]

Una de las peticiones que más se repiten es la inclusión en la biblioteca estándar de C++ de las tablas hash (contenedores asociativos sin orden). No fue adoptada en el estándar actual solo debido a restricciones de tiempo. Aunque esta solución es menos eficiente que un árbol balanceado en el peor caso (en presencia de muchas colisiones), en muchas aplicaciones verdaderas se desempeña mejor.

Las colisiones serán manejadas solo a través de encadenamiento lineal porque el comité no considera oportuno estandardizar soluciones de direccionamiento abierto que introducen bastantes de problemas intrínsecos (sobre todo cuando es admitido el borrado de elementos). Para evitar los choques de nombres con las bibliotecas no estándar que desarrollaran sus propias implementaciones de la tabla hash, será utilizado el prefijo "unordered" en vez del "hash".

La nueva utilidad tendrá cuatro tipos de tablas hash, diferenciados en si aceptan o no elementos con la misma clave (claves únicas o claves equivalentes), y si mapean cada clave a un valor asociado.

Tipo de tabla hash Valores asociados Claves equivalentes
std::unordered_set No No No No
std::unordered_multiset No No Sí 
std::unordered_map Sí  No No
std::unordered_multimap Sí  No No

Las nuevas clases satisfacen todos los requisitos de una clase container, y tienen todos los métodos necesarios para acceder elementos: insert, erase, begin, end.

Esta nueva utilidad no necesita ninguna extensión del núcleo del lenguaje de C++, solamente una pequeña extensión del encabezado <functional> y la introducción de encabezados <unordered_set> y <unordered_map>, (aunque la implementación tomará ventajas de varias características del lenguaje C++11). No son necesarios otros cambios a cualquier clase estándar existente, y no depende de ninguna de las otras extensiones de la biblioteca estándar.

Expresiones regulares

[editar]

Muchas bibliotecas estandarizadas fueron creadas para manejar expresiones regulares. Puesto que el uso de estos algoritmos es muy común, la biblioteca estándar los incluirá usando todas las potencialidades de un lenguaje orientado a objetos.

La nueva biblioteca, definida en el nuevo encabezado <regex>, está hecha de un par de clases:

  • Las expresiones regulares son representadas por la instancia de la clase de plantilla std::regex;
  • Las ocurrencias son representadas por la instancia de la clase de plantilla std::match_results.

La función std::regex_search es usada para buscar, mientras que para ‘búsqueda y reemplazo’ es usada la función std::regex_replace, la cual retorna un nuevo string. Los algoritmos std::regex_search y std::regex_replace toman una expresión regular y un string y escriben las ocurrencias encontradas en la estructura std::match_results.

Aquí hay un ejemplo en el uso del std::match_results:

const char *reg_esp = "[ ,.\\t\\n;:]";  // List of separator characters.

// this can be done using raw string literals:
// const char *reg_esp = R"([ ,.\t\n;:])";

std::regex rgx(reg_esp);  // 'regex' es una instancia de la clase de template
                          // 'basic_regex' con argumento de tipo 'char'.
std::cmatch match;        // 'cmatch' es una instancia de la clase de template
                          // 'match_results' con argumento de tipo 'const char *'.
const char *target = "Unseen University - Ankh-Morpork";

// Identifies all words of 'target' separated by characters of 'reg_esp'.
if( std::regex_search( target, match, rgx ) ) {
    // If words separated by specified characters are present.

    const size_t n = match.size();
    for( size_t a = 0; a < n; a++ ) {
        std::string str( match[a].first, match[a].second );
        std::cout << str << "\n";
    }
}

Note que se usan dobles barras inversas debido a que C++ usa la barra inversa simple como un carácter de escape. La característica raw string del C++11 pudiera usarse para evitar este problema.

La biblioteca <regex> no requiere ni la alteración de cualquier encabezado existente, (aunque los usará cuando sea apropiado), ni una extensión del núcleo del lenguaje.

Punteros inteligentes de propósito general

[editar]

C++11 proporciona el std::unique_ptr, como también mejoras al std::shared_ptr y std::weak_ptr del TR1. std::auto_ptr es obsoleto.

Facilidad de número random extensible

[editar]

La biblioteca estándar de C proporciona la capacidad de generar números pseudaleatorios (random) a través de la función rand. Sin embargo, el algoritmo es delegado enteramente al proveedor de la biblioteca. C++ heredó esta funcionalidad sin cambios, pero C++11 proporcionará un nuevo método para generar números pseudaleatorios.

La funcionalidad del número al azar (random) de C++11 está dividida en dos partes: un motor generador que contiene el estado del generador de número al azar y produce los números pseudaleatorios; y una distribución, que determina el rango y la distribución matemática del resultado. Estos dos son combinados para formar un objeto generador de números al azar.

A diferencia del rand estándar de C, el mecanismo del C++11 vendrá con tres algoritmos de motor generador, cada uno con sus propias fuerzas y debilidades:

Plantilla de clase Entero/Punto flotante Calidad Velocidad Tamaño del estado
linear_congruential Entero Mediana Mediana 1
subtract_with_carry Ambos Mediana Rápida 25
mersenne_twister Entero Buena Rápida 624

C++11 también proporcionará un número de distribuciones estándar: uniform_int_distribution, bernoulli_distribution, geometric_distribution, poisson_distribution, binomial_distribution, uniform_real_distribution, exponential_distribution, normal_distribution, y gamma_distribution

El generador y las distribuciones son combinados como en el ejemplo siguiente:

#include <random>
#include <functional>

std::uniform_int_distribution<int> distribution(0, 99);
std::mt19937 engine; // Mersenne twister MT19937
auto generator = std::bind(distribution, engine);
int random = generator();  // Genera una variable entera uniforme entre 0 y 99.
int random2 = distribution(engine); // Genera otra muestra usando directamente los objetos distribution y engine.

Referencia wrapper

[editar]

Una referencia wrapper (envoltura) es obtenida de una instancia de la clase de plantilla reference_wrapper. Las referencias wrapper son similares a las referencias normales (‘&’) del lenguaje C++. Para obtener una referencia wrapper de cualquier objeto es usada la referencia de plantilla de función ref (para una referencia constante es usado cref).

Las referencias wrapper son útiles sobre todo para las plantillas de función, donde son necesarias las referencias a los parámetros en vez de a las copias:

// Esta función obtendrá una referencia al parámetro 'r' y lo incrementará.
void f (int &r)  { r++; }

// Función de template.
template<class F, class P> void g (F f, P t)  { f(t); }

int main()
{
    int i = 0 ;
    g (f, i) ;  // Es instanciado 'g<void (int &r), int>'
               // entonces 'i' no será modificado.
    std::cout << i << std::endl;  // Salida -> 0

    g (f, std::ref(i));  // Es instanciado 'g<void(int &r),reference_wrapper<int>>'
                    // entonces 'i' será modificado.
    std::cout << i << std::endl;  // Salida -> 1
}

Esta nueva utilidad será agregada al encabezado existente <utility> y no necesita otras extensiones del lenguaje C++.

Envoltorios polimórficos para objetos de función

[editar]

Los envoltorios polimórficos para los objetos de función son similares a los punteros de función en la semántica y la sintaxis, pero son menos fuertemente enlazados (tightly bound) y pueden referir indiscriminadamente cualquier cosa que puede ser llamada (punteros de función, punteros de miembro de función, o functors) cuyos argumentos son compatibles con las del envoltorio.

Con el ejemplo es posible entender sus características:

std::function<int (int, int)> func;  // Creación de envoltorio (wrapper)
                                     // usando la clase de plantilla 'function'.
std::plus<int> add;  // 'plus' es declarado como 'template<class T> T plus( T, T ) ;'
                     // entonces, 'add' es de tipo 'int add( int x, int y )'.
func = &add;         // OK - Los parámetros y el tipo de retorno son el mismo.

int a = func (1, 2);  // NOTA: si el envoltorio 'func' no se refiere a ninguna función,
                      // es lanzada la excepción 'std::bad_function_call'.

std::function<bool (short, short)> func2 ;
if(!func2) { // Verdadero porque a 'func2' todavía no se le ha asignado una función.

    bool adjacent(long x, long y);
    func2 = &adjacent ;  // OK - Los parámetros y los tipos de retorno son convertibles.

    struct Test {
        bool operator()(short x, short y);
    };
    Test car;
    func = std::ref(car);  // 'std::ref' es una función de plantilla que retorna el envoltorio
                           // de función de miembro 'operator()' de struct 'car'.
}
func = func2;  // OK -  Los parámetros y los tipos de retorno son convertibles.

La función de clase de plantilla será definida dentro del encabezado <functional>, y no requiere ningún cambio al lenguaje C++.

Type traits para metaprogramación

[editar]

La metaprogramación consiste en crear un programa que crea o modifica otro programa (o a sí mismo). Esto puede suceder durante la compilación o durante la ejecución. El comité de estándares de C++ ha decidido introducir una biblioteca que permite la metaprogramación durante la compilación a través de plantillas.

Aquí hay un ejemplo de un meta-programa, usando el estándar actual de C++03: una recursión de instancias de plantillas para calcular exponentes enteros:

template<int B, int N>
struct Pow {
    // llamada recursiva y recombinación.
    enum{ value = B*Pow<B, N-1>::value };
};

template< int B >
struct Pow<B, 0> {
    // ''N == 0'' condición de terminación.
    enum{ value = 1 };
};
int quartic_of_three = Pow<3, 4>::value;

Muchos algoritmos pueden operar en diferentes tipos de datos; Las plantillas de C++ soportan la programación genérica y hacen el código más compacto y útil. Sin embargo es común para los algoritmos necesitar información sobre los tipos de datos que son usados. Esta información puede ser extraída durante la instanciación de una clase de plantilla usando type traits.

El type traits puede identificar la categoría de un objeto y todas las características de una clase (o de un struct). Son definidos en el nuevo encabezado <type_traits>..

En el ejemplo siguiente está la función de plantilla 'elaborate' que, dependiendo de los tipos de datos dados, instanciará uno de los dos algoritmos propuestos (algorithm.do_it).

// Primera forma de operar.
template< bool B > struct Algorithm {
    template<class T1, class T2> static int do_it (T1 &, T2 &)  { /*...*/ }
};

// Segunda forma de operar.
template<> struct Algorithm<true> {
    template<class T1, class T2> static int do_it (T1, T2)  { /*...*/ }
};

// Instanciando 'elaborate' instanciará automáticamente la forma correcta de operar.
template<class T1, class T2>
int elaborate (T1 A, T2 B)
{
    // Usa la segunda forma solo si 'T1' es un entero y si 'T2' está
    // en punto flotante, de lo contrario usa la primera forma.
    return Algorithm<std::is_integral<T1>::value && std::is_floating_point<T2>::value>::do_it( A, B ) ;
}

A través del type traits, definido en encabezado <type_transform>, es también posible crear operaciones de transformación de tipos (static_cast y const_cast son insuficientes dentro de una plantilla).

Este tipo de programación produce un código elegante y conciso; sin embargo el punto débil de estas técnicas es la depuración: incómodo durante la compilación y muy difícil durante la ejecución del programa.

Método uniforme para computar el tipo de retorno de objetos de función

[editar]

La determinación del tipo de retorno de un objeto de función de plantilla en tiempo de compilación no es intuitiva, particularmente si el valor de retorno depende de los parámetros de la función. Como un ejemplo:

struct Clear {
    int    operator()(int);     // El tipo del parámetro es
    double operator()(double);  // igual al tipo de retorno.
};

template <class Obj>
class Calculus {
public:
    template<class Arg> Arg operator()(Arg& a) const
    {
        return member(a);
    }
private:
    Obj member;
};

Instanciar la plantilla de clase Calculus<Clear>, el objeto de función de calculus tendrá siempre el mismo tipo de retorno que el objeto de función de Clear.. Sin embargo, dada la clase Confused de abajo:

struct Confused {
    double operator()(int);     // El tipo del parámetro NO es
    int    operator()(double);  // igual al tipo de retorno.
};

El intentar instanciar Calculus<Confused> causará que el tipo de retorno de Calculus no sea igual que el de clase Confused. El compilador puede generar advertencias acerca de la conversión desde int a double y viceversa.

El TR1 introduce, y C++11 adopta, la clase de plantilla std::result_of que permite que uno determine y que use el tipo de retorno de un objeto de función para cada declaración. El objeto CalculusVer2 usa al objeto std::result_of para derivar el tipo de retorno del objeto de función:

template< class Obj >
class CalculusVer2 {
public:
    template<class Arg>
    typename std::result_of<Obj(Arg)>::type operator()(Arg& a) const
    {
        return member(a);
    }
private:
    Obj member;
};

De esta manera en instancias del objeto de función CalculusVer2<Confused> no hay conversiones, advertencias, o errores.

El único cambio de la versión TR1 del std::result_of es que la versión TR1 permitía a una implementación fallar para poder determinar el tipo del resultado de una llamada de función. Debido a los cambios a C++ para soportar decltype, la versión C++11 del std::result_of ya no necesita estos casos especiales; son requeridas las implementaciones para computar un tipo en todos los casos.

Características eliminadas u obsoletas

[editar]

Movidas a otro reporte:

  • Módulos
  • Tipos Decimales
  • Funciones Matemáticas Especiales

Pospuestas:

Características eliminadas u obsoletas

[editar]
  • El término punto de secuencia, que será reemplazado al especificar que una operación es secuenciada después de otra, o que dos operaciones no son secuenciales[8]
  • Especificaciones de excepciones dinámicas. Con la palabra clave noexcept está disponible la especificación en tiempo de compilación de no excepción lanzando funciones (útil para optimización)
  • std::auto_ptr Reemplazado por std::unique_ptr.
  • Clases base de función objeto (std::unary_function, std::binary_function), adaptadores a punteros a funciones u adaptadores a punteros a miembros, clases binder.

Lectura adicional

[editar]

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

[editar]
  • Doc No. 1401: Jan Kristoffersen (21 October 2002) Atomic operations with multi-threaded environments
  • Doc No. 1402: Doug Gregor (22 October 2002) A Proposal to add a Polymorphic Function Object Wrapper to the Standard Library
  • Doc No. 1403: Doug Gregor (8 November 2002) Proposal for adding tuple types into the standard library
  • Doc No. 1424: John Maddock (3 March 2003) A Proposal to add Type Traits to the Standard Library
  • Doc No. 1429: John Maddock (3 March 2003) A Proposal to add Regular Expression to the Standard Library
  • Doc No. 1449: B. Stroustrup, G. Dos Reis, Mat Marcus, Walter E. Brown, Herb Sutter (7 April 2003) Proposal to add template aliases to C++
  • Doc No. 1450: P. Dimov, B. Dawes, G. Colvin (27 March 2003) A Proposal to Add General Purpose Smart Pointers to the Library Technical Report (Revision 1)
  • Doc No. 1452: Jens Maurer (10 April 2003) A Proposal to Add an Extensible Random Number Facility to the Standard Library (Revision 2)
  • Doc No. 1453: D. Gregor, P. Dimov (9 April 2003) A proposal to add a reference wrapper to the standard library (revision 1)
  • Doc No. 1454: Douglas Gregor, P. Dimov (9 April 2003) A uniform method for computing function object return types (revision 1)
  • Doc No. 1456: Matthew Austern (9 April 2003) A Proposal to Add Hash Tables to the Standard Library (revision 4)
  • Doc No. 1471: Daveed Vandevoorde (18 April 2003) Reflective Metaprogramming in C++
  • Doc No. 1676: Bronek Kozicki (9 September 2004) Non-member overloaded copy assignment operator
  • Doc No. 1704: Douglas Gregor, Jaakko Järvi, Gary Powell (10 September 2004) Variadic Templates: Exploring the Design Space
  • Doc No. 1705: J. Järvi, B. Stroustrup, D. Gregor, J. Siek, G. Dos Reis (12 September 2004) Decltype (and auto)
  • Doc No. 1717: Francis Glassborow, Lois Goldthwaite (5 November 2004) explicit class and default definitions
  • Doc No. 1719: Herb Sutter, David E. Miller (21 October 2004) Strongly Typed Enums (revision 1)
  • Doc No. 1720: R. Klarer, J. Maddock, B. Dawes, H. Hinnant (20 October 2004) Proposal to Add Static Assertions to the Core Language (Revision 3)
  • Doc No. 1757: Daveed Vandevoorde (14 de enero de 2005) Right Angle Brackets (Revision 2)
  • Doc No. 1811: J. Stephen Adamczyk (29 April 2005) Adding the long long type to C++ (Revision 3)
  • Doc No. 1815: Lawrence Crowl (2 May 2005) ISO C++ Strategic Plan for Multithreading
  • Doc No. 1827: Chris Uzdavinis, Alisdair Meredith (29 August 2005) An Explicit Override Syntax for C++
  • Doc No. 1834: Detlef Vollmann (24 June 2005) A Pleading for Reasonable Parallel Processing Support in C++
  • Doc No. 1836: ISO/IEC DTR 19768 (24 June 2005) Draft Technical Report on C++ Library Extensions
  • Doc No. 1886: Gabriel Dos Reis, Bjarne Stroustrup (20 October 2005) Specifying C++ concepts
  • Doc No. 1891: Walter E. Brown (18 October 2005) Progress toward Opaque Typedefs for C++0X
  • Doc No. 1898: Michel Michaud, Michael Wong (6 October 2004) Forwarding and inherited constructors
  • Doc No. 1919: Bjarne Stroustrup, Gabriel Dos Reis (11 December 2005) Initializer lists
  • Doc No. 1968: V Samko; J Willcock, J Järvi, D Gregor, A Lumsdaine (26 February 2006) Lambda expressions and closures for C++
  • Doc No. 1986: Herb Sutter, Francis Glassborow (6 April 2006) Delegating Constructors (revision 3)
  • Doc No. 2016: Hans Boehm, Nick Maclaren (21 April 2002) Should volatile Acquire Atomicity and Thread Visibility Semantics?
  • Doc No. 2142: ISO/IEC DTR 19768 (12 de enero de 2007) State of C++ Evolution (between Portland and Oxford 2007 Meetings)
  • Doc No. 2228: ISO/IEC DTR 19768 (3 May 2007) State of C++ Evolution (Oxford 2007 Meetings)
  • Doc No. 2258: G. Dos Reis and B. Stroustrup Templates Aliases
  • Doc No. 2280: Lawrence Crowl (2 May 2007) Thread-Local Storage
  • Doc No. 2291: ISO/IEC DTR 19768 (25 June 2007) State of C++ Evolution (Toronto 2007 Meetings)
  • Doc No. 2336: ISO/IEC DTR 19768 (29 July 2007) State of C++ Evolution (Toronto 2007 Meetings)
  • Doc No. 2389: ISO/IEC DTR 19768 (7 August 2007) State of C++ Evolution (pre-Kona 2007 Meetings)
  • Doc No. 2431: SC22/WG21/N2431 = J16/07-0301 (2 October 2007), A name for the null pointer: nullptr
  • Doc No. 2432: ISO/IEC DTR 19768 (23 October 2007) State of C++ Evolution (post-Kona 2007 Meeting)
  • Doc No. 2437: Lois Goldthwaite (5 October 2007) Explicit Conversion Operators
  • Doc No. 2461: ISO/IEC DTR 19768 (22 October 2007) Working Draft, Standard for programming Language C++
  • Doc No. 2507: ISO/IEC DTR 19768 (4 February 2008) State of C++ Evolution (pre-Bellevue 2008 Meeting)
  • Doc No. 2565: ISO/IEC DTR 19768 (7 March 2008) State of C++ Evolution (post-Bellevue 2008 Meeting)
  • Doc No. 2597: ISO/IEC DTR 19768 (29 April 2008) State of C++ Evolution (pre-Antipolis 2008 Meeting)
  • Doc No. 2606: ISO/IEC DTR 19768 (19 May 2008) Working Draft, Standard for Programming Language C++
  • Doc No. 2798: ISO/IEC DTR 19768 (4 October 2008) Working Draft, Standard for Programming Language C++
  • Doc No. 2857: ISO/IEC DTR 19768 (23 March 2009) Working Draft, Standard for Programming Language C++
  • Doc No. 2869: ISO/IEC DTR 19768 (28 April 2009) State of C++ Evolution (post-San Francisco 2008 Meeting)
  • Doc No. 3014: Stephen D. Clamage (4 November 2009) AGENDA, PL22.16 Meeting No. 53, WG21 Meeting No. 48, 8–13 March 2010, Pittsburgh, PA
  • Doc No. 3082: Herb Sutter (13 March 2010) C++0x Meeting Schedule
  • Doc No. 3092: ISO/ISC DTR 19769 (26 March 2010) Working Draft, Standard for Programming Language C++
  • Doc No. 3126: ISO/ISC DTR 19769 (21 August 2010) Working Draft, Standard for Programming Language C++
  • Doc No. 3225: ISO/ISC DTR 19769 (27 November 2010) Working Draft, Standard for Programming Language C++
  • Doc No. 3242: ISO/ISC DTR 19769 (28 February 2011) Working Draft, Standard for Programming Language C++
  • Doc No. 3291: ISO/ISC DTR 19769 (5 April 2011) Working Draft, Standard for Programming Language C++
  • Doc No. 3290: ISO/ISC DTR 19769 (5 April 2011) FDIS, Standard for Programming Language C++

Artículos

[editar]

Véase también

[editar]

Notas

[editar]

Referencias

[editar]
  1. Doc No. 2697: ISO/IEC DTR 19768 (15 June 2008) Minutes of WG21 Meeting 8–15 June 2008
  2. Doc No. 2544: Alan Talbot, Lois Goldthwaite, Lawrence Crowl, Jens Maurer (29 February 2008) Unrestricted unions
  3. Doc No. 3000: ISO/ISC DTR 19769 (9 November 2009) Working Draft, Standard for Programming Language C++
  4. ISO/IEC (2003). ISO/IEC 14882:2003(E): Programming Languages - C++ §3.2 One definition rule [basic.def.odr] para. 3
  5. http://gcc.gnu.org/onlinedocs/gcc/Long-Long.html
  6. http://msdn.microsoft.com/en-us/library/s3f49ktz(VS.80).aspx
  7. Milewski, Bartosz (3 de marzo de 2009). «Broken promises–C++0x futures». Consultado el 24 de enero de 2010. 
  8. Caves, Jonathan (4 de junio de 2007). «Update on the C++-0x Language Standard». Consultado el 25 de mayo de 2010. 

Enlaces externos

[editar]