¿Qué cambios importantes se introducen en C ++ 11?


227

Sé que al menos uno de los cambios en C ++ 11 que hará que algún código antiguo deje de compilarse: la introducción de explicit operator bool()en la biblioteca estándar, la sustitución de instancias antiguas de operator void*(). De acuerdo, el código que esto romperá es probablemente un código que no debería haber sido válido en primer lugar, pero aún así es un cambio importante: los programas que solían ser válidos ya no lo son.

¿Hay otros cambios importantes?


1
¿Eliminar el significado de la exportpalabra clave? Me traeré el abrigo.
Steve Jessop

77
Sabes, no lo llamaría el cambio de conversión a bool como un "cambio radical" ... más como un "cambio castigador".
Xeo

44
Cuando todo el papeleo necesario para crear tal unión solo está esperando ser sellado, claro, ¿por qué no?
Dennis Zickefoose

3
@Xeo: mystream.good()¿no es lo mismo que bool(mystream)? good()es verdadero si no se establece ninguna bandera. bool(mystream)sigue siendo falso si solo eofbitestá configurado. !mystream.fail()Sería el equivalente correcto.
R. Martinho Fernandes

2
Nota para el moderador : " Por favor, mantenga los comentarios sobre el tema con la pregunta o la respuesta a la mano. Al discutir una pregunta o respuesta, la discusión debe ser solo sobre eso, la pregunta o la respuesta a la mano. El debate, en general, no es constructivo para Stack Overflow. Antagonizar seguramente no lo es. "
Tim Post

Respuestas:


178

El FDIS tiene una sección para incompatibilidades, en el apéndice C.2"C ++ e ISO C ++ 2003".

Resumen, parafraseando el FDIS aquí, para que sea (mejor) adecuado como respuesta SO. Agregué algunos ejemplos propios para ilustrar las diferencias.

Hay algunas incompatibilidades relacionadas con la biblioteca en las que no sé exactamente las implicaciones de, por lo que las dejo para que otros las expliquen.

Lenguaje central


#define u8 "abc"
const char *s = u8"def"; // Previously "abcdef", now "def"

#define _x "there"
"hello"_x // now a user-defined-string-literal. Previously, expanded _x .

Nuevas palabras clave: alignas, alignof, char16_t, char32_t, constexpr, decltype, noexcept, nullptr, static_assert y thread_local


Ciertos literales enteros más grandes que pueden ser representados por long podrían cambiar de un tipo entero sin signo a long largo con signo.


El código válido de C ++ 2003 que usa división entera redondea el resultado hacia 0 o hacia el infinito negativo, mientras que C ++ 0x siempre redondea el resultado hacia 0.

(Es cierto que no es realmente un problema de compatibilidad para la mayoría de las personas).


El código válido de C ++ 2003 que usa la palabra clave autocomo un especificador de clase de almacenamiento puede no ser válido en C ++ 0x.


Las conversiones estrechas causan incompatibilidades con C ++ 03. Por ejemplo, el siguiente código es válido en C ++ 2003 pero no válido en esta Norma Internacional porque double to int es una conversión de reducción:

int x[] = { 2.0 };

Las funciones de miembros especiales declaradas implícitamente se definen como eliminadas cuando la definición implícita se hubiera formado mal.

Un programa válido de C ++ 2003 que utiliza una de estas funciones especiales para miembros en un contexto en el que no se requiere la definición (por ejemplo, en una expresión que no se evalúa potencialmente) se vuelve mal formado.

Ejemplo por mi parte:

struct A { private: A(); };
struct B : A { };
int main() { sizeof B(); /* valid in C++03, invalid in C++0x */ }

Tal tamaño de trucos han sido utilizados por algunos SFINAE, y deben cambiarse ahora :)


Los destructores declarados por el usuario tienen una especificación de excepción implícita.

Ejemplo por mi parte:

struct A {
  ~A() { throw "foo"; }
};

int main() { try { A a; } catch(...) { } }

Este código llama terminateen C ++ 0x, pero no en C ++ 03. Porque la especificación de excepción implícita de A::~Aen C ++ 0x es noexcept(true).


Una declaración válida de C ++ 2003 que contiene exportestá mal formada en C ++ 0x.


Una expresión válida de C ++ 2003 que contiene >seguida inmediatamente por otra >ahora puede tratarse como cierre de dos plantillas.

En C ++ 03, >>siempre sería el token shift-operator.


Permitir llamadas dependientes de funciones con enlace interno.

Ejemplo por mi parte:

static void f(int) { }
void f(long) { }

template<typename T>
void g(T t) { f(t); }

int main() { g(0); }

En C ++ 03, esto llama f(long), pero en C ++ 0x, esto llama f(int). Cabe señalar que tanto en C ++ 03 como en C ++ 0x, las siguientes llamadas f(B)(el contexto de instanciación solo considera las declaraciones de enlaces externos).

struct B { };
struct A : B { };

template<typename T>
void g(T t) { f(t); }

static void f(A) { }
void f(B) { }

int main() { A a; g(a); }

La mejor coincidencia f(A)No se toma la , ya que no tiene enlace externo.


Cambios de biblioteca

El código válido de C ++ 2003 que utiliza cualquier identificador agregado a la biblioteca estándar de C ++ de C ++ 0x puede fallar al compilar o producir resultados diferentes en este estándar internacional.


El código válido de C ++ 2003 que los #includesencabezados con nombres de nuevos encabezados de biblioteca estándar C ++ 0x pueden no ser válidos en este estándar internacional.


El código válido de C ++ 2003 que se ha compilado esperando que el intercambio esté <algorithm>incluido puede incluir<utility>


El espacio de nombres global posixahora está reservado para la estandarización.


Válido código C ++ 2003 que define override, final, carries_dependency, o noreturncomo macros no son válidos en C ++ 0x.


"Permitir llamadas dependientes de funciones con enlace interno". ¿Podría por favor explicar la diferencia entre sus dos ejemplos? Claramente me estoy perdiendo algo.
Dennis Zickefoose

@Dennis el cambio fue introducido por open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#561 . Aunque no comentan sobre el hecho, el "contexto de instanciación" todavía consiste en "el conjunto de declaraciones con vinculación externa declaradas antes del punto de instanciación de la especialización de plantilla en la misma unidad de traducción". Entonces, el cambio que hicieron solo afecta la búsqueda en el contexto de definición.
Johannes Schaub - litb

En mi primer ejemplo dado, la función de enlace interno era visible y se encontraba en el contexto de definición de la plantilla. En mi segundo ejemplo, la función de vinculación interna debería ser parte del contexto de instanciación que se encuentra. Pero como no lo es, no se puede encontrar.
Johannes Schaub - litb

Por cierto, creo que el único caso en el que es seguro que un contexto de definición de plantilla encuentre una función con enlace interno es cuando la especialización de plantilla de función solo se instancia explícitamente en una TU (donde se define la plantilla), y todas las demás TU se basan en esa instanciación explícita. En todos los demás casos (donde las otras TU crearían instancias de la especialización ellos mismos), violaría el ODR al hacer que la definición de plantilla use una función diferente (enlace interno) cada vez.
Johannes Schaub - litb

Entonces, no estoy seguro de por qué mantuvieron la restricción en el contexto de instanciación: solo habría una instanciación (explícita), y esa instancia usaría funciones de enlace interno que se encuentran en el contexto de instanciación de la TU de instanciación. Al igual que lo haría para el contexto de definición. Por cierto, creo que si aún lo hubiéramos hecho export, creo que las otras TU no tendrían que depender de la instanciación explícita, sino que podrían crear una instancia de la plantilla. Entonces marcaría una diferencia si las funciones de vinculación interna son visibles en el contexto de instanciación.
Johannes Schaub - litb

28

El significado de la palabra clave automática cambió.


99
Si ha estado usando la autopalabra clave, algo está muy mal con su código. ¿Por qué demonios lo usarías?
Elazar Leibovich

Eso no es un cambio radical . Cada uso válido de C ++ 03 autosigue siendo válido en C ++ 11.
Drew Dormann

11
@DrewDormann int main() { auto int i = 0; return i; }es C ++ 03 perfectamente válido, pero es un error de sintaxis en C ++ 11. La única advertencia que puedo hacer que los compiladores den en modo C ++ 03 es una advertencia sobre compatibilidad.

24

Rompiendo el cambio?

Bueno, por un lado, si se ha utilizado decltype, constexpr, nullptr, etc, como identificadores entonces usted puede estar en problemas ...


21

Algunas incompatibilidades principales que no están cubiertas por la sección de incompatibilidades:


C ++ 0x trata el nombre de la clase inyectada como una plantilla, si el nombre se pasa como un argumento a un parámetro de plantilla de plantilla, y como un tipo si se pasa a un parámetro de tipo de plantilla.

El código válido de C ++ 03 puede comportarse de manera diferente si se basa en que el nombre de la clase inyectada sea siempre un tipo en estos escenarios. Código de ejemplo tomado de mi PR clang

template<template<typename> class X>
struct M { };

template<template<typename> class X>
void g(int = 0); // #1

template<typename T>
void g(long = 0); // #2

template<typename T>
struct A {
  void f() {
    g<A>(); /* is ambiguous in C++0x */
    g<A>(1); /* should choose #1 in C++0x */
  }
};

void h() {
  A<int> a;
  a.f();
}

En C ++ 03, el código llama al segundo las gdos veces.


C ++ 0x hace que algunos nombres que eran dependientes en C ++ 03 ahora no sean dependientes. Y requiere la búsqueda de nombres para nombres calificados no dependientes que se refieren a miembros de la plantilla de clase actual para que se repitan en la instanciación, y requiere la verificación de que estos nombres buscan de la misma manera que en el contexto de definición de plantilla.

Es posible que el código C ++ 03 válido que depende de la regla de dominio ya no se compile debido a este cambio.

Ejemplo:

struct B { void f(); };

template<typename T>
struct A : virtual B { void f(); };

template<typename T>
struct C : virtual B, A<T> {
  void g() { this->f(); }
};

int main() { C<int> c; c.g(); }

Este código válido de C ++ 03 que llama A<int>::fno es válido en C ++ 0x, porque la búsqueda de nombres al crear instancias se encuentra A<int>::fen lugar de B::f, lo que causa un conflicto con la búsqueda en definición.

En este punto, no está claro si eso es un defecto en el FDIS. El comité es consciente de esto y evaluará la situación.


Una declaración de uso donde la última parte es la misma que el identificador en la última parte del calificador en el nombre calificado que denota una clase base, que la declaración de uso ahora nombra al constructor, en lugar de miembros con ese nombre.

Ejemplo:

struct A { protected: int B; };
typedef A B;

struct C : B {
  // inheriting constructor, instead of bringing A::B into scope
  using B::B;
};

int main() { C c; c.B = 0; }

El código de ejemplo anterior está bien formado en C ++ 03, pero está mal formado en C ++ 0x, ya A::Bque todavía no se puede acceder a él main.


14

La falla de extracción de corriente se trata de manera diferente.

Ejemplo

#include <sstream>
#include <cassert>

int main()
{
   std::stringstream ss;
   ss << '!';
   
   int x = -1;
   
   assert(!(ss >> x)); // C++03 and C++11
   assert(x == -1);    // C++03
   assert(x == 0);     // C++11
}

Propuesta de cambio

http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2011/n3246.html#23

Referencia estándar

[C++03: 22.2.2.1.2/11]: El resultado del procesamiento de la etapa 2 puede ser uno de

  • Se ha acumulado una secuencia de caracteres en la etapa 2 que se convierte (de acuerdo con las reglas de scanf) a un valor del tipo de val. Este valor se almacena valy ios_base::goodbitse almacena en err.
  • La secuencia de caracteres acumulados en la etapa 2 habría provocado scanfun error de entrada. ios_base::failbitse asigna a err. [ed: no hay nada almacenado val]

[C++11: 22.4.2.1.2/3]: [..] El valor numérico a almacenar puede ser uno de:

  • cero, si la función de conversión no puede convertir todo el campo . ios_base::failbitse asigna a err.
  • el valor representable más positivo, si el campo representa un valor positivo demasiado grande para ser representado val. ios_base::failbitse asigna a err.
  • el valor representable más negativo o cero para un tipo entero sin signo, si el campo representa un valor negativo demasiado grande para representarlo val. ios_base::failbitse asigna a err.
  • el valor convertido, de lo contrario.

El valor numérico resultante se almacena en val.

Implementaciones

  • GCC 4.8 da salida correctamente a C ++ 11 :

    La afirmación `x == -1 'falló

  • GCC 4.5-4.8 todos los resultados para C ++ 03 lo siguiente, lo que parece ser un error:

    La afirmación `x == -1 'falló

  • Visual C ++ 2008 Express produce correctamente los resultados para C ++ 03:

    Afirmación fallida: x == 0

  • Visual C ++ 2012 Express genera incorrectamente resultados para C ++ 11, lo que parece ser un problema de estado de implementación:

    Afirmación fallida: x == 0


13

¿Cómo es la introducción de operadores de conversión explícitos un cambio radical? La versión anterior seguirá siendo tan "válida" como antes.

Sí, el cambio de operator void*() consta explicit operator bool() constserá un cambio radical, pero solo si se usa de una manera incorrecta dentro y fuera de sí mismo. El código conforme no se romperá.

Ahora, otro cambio importante es la prohibición de reducir las conversiones durante la inicialización agregada :

int a[] = { 1.0 }; // error

Editar : Solo recuerdo, std::identity<T>se eliminará en C ++ 0x (ver la nota). Es una estructura conveniente para hacer que los tipos sean dependientes. Dado que la estructura realmente no hace mucho, esto debería solucionarlo:

template<class T>
struct identity{
  typedef T type;
};

Si los objetos de la biblioteca estándar tienen conversiones explícitas agregadas, entonces las conversiones implícitas existentes pueden dejar de funcionar. Pero no puedo imaginar un escenario en el que la conversión no sea válida y haga algo útil.
Dennis Zickefoose

La introducción es un cambio radical porque va a reemplazar lo existente operator void*.
R. Martinho Fernandes

@Dennis: Aaah, ahora veo a qué se refería @Martinho. Pero solo será un cambio radical si la gente lo usara de otra manera.
Xeo

"pero solo si se usa de una manera que está mal dentro y fuera de sí mismo": bool ok = cin >> a; cout << "done reading" << endl; if (ok) { ... }no hay nada realmente malo en eso en C ++ 03, pero se ha convertido en un error en C ++ 11. (Nota: GCC 4.9 todavía tiene operator void*() constaquí, por lo que acepta el código en C ++ 11 Modo.)

std::identity<T>no se eliminó en C ++ 11, porque no era parte de C ++ 03. Existió brevemente en el borrador para C ++ 11, y se eliminó del borrador antes de la estandarización.
Howard Hinnant


7

Se ha discutido mucho sobre el movimiento implícito que rompe la compatibilidad con versiones anteriores

( una página anterior con discusión relevante )

Si lee los comentarios, el retorno de movimiento implícito también es un cambio radical.


El resultado de esas discusiones es que se eliminó en casi todos los casos. ¿Hay algún problema con lo que queda?
Dennis Zickefoose

@ Dennis: Sí. Su pregunta ya fue formulada, respondida y debatida hasta la muerte en esta página de seguimiento
Ben Voigt

Ahh, la página móvil no mostró los comentarios. De cualquier manera, ese es el enlace mucho más útil ... Las rarezas históricas del proceso de estandarización no son tan relevantes (a menos que esté usando MSVC, que creo que usa ese primer borrador).
Dennis Zickefoose

@ Dennis: Creo que tienes razón. Moví los enlaces alrededor de algunos en mi respuesta.
Ben Voigt

Lamentablemente, cpp-next.com ya no existe. Para referencia futura, estas son páginas guardadas por web.archive.org: movimiento implícito que rompe la compatibilidad con versiones anteriores y una página anterior con discusión relevante .
Max Truxa

6
struct x {
   x(int) {}
};

void f(auto x = 3) { }

int main() {
   f();
}

C ++ 03: válido.

C ++ 0x: error: parameter declared 'auto'


2
@Xeo: el código es válido en C ++ 03. Es un parámetro con tipo struct xy sin nombre.
Ben Voigt

Esperaba atrapar a alguien. ¡Solo desearía que @Xeo no hubiera sido tan rápido para eliminar su comentario, ya que no pude leerlo!
ligereza corre en órbita el

@Xeo: Sin profundizar en la gramática, estoy seguro de que auto no es una palabra clave válida allí. Si lo fuera, probablemente funcionaría como esperaba, pero eso probablemente sea realmente difícil de definir correctamente.
Dennis Zickefoose

Digamos que me atrapaste. Literalmente ignoró la estructura. :)
Xeo

@Tomalek: Xeo había señalado correctamente que C ++ 03 no tiene int implícito.
Ben Voigt

-4

Características del lenguaje

  1. Inicialización uniforme y general usando {}
  2. auto
  3. Prevención de estrechamiento
  4. constexpr
  5. Rango basado para bucle
  6. nullptr
  7. clase enum
  8. static_assert
  9. std :: initializer_list
  10. Referencias de valor (semántica de movimiento)
  11. >>
  12. Lambdas
  13. Plantillas variadas
  14. Alias ​​de tipo y plantilla
  15. Caracteres Unicode
  16. tipo entero largo largo
  17. Alignas y Alignof
  18. decltype
  19. Literales de cadena sin procesar
  20. POD generalizado
  21. Sindicatos generalizados
  22. Clases locales como argumentos de plantilla
  23. Sintaxis de tipo de retorno de sufijo
  24. [[carry_dependency]] y [[noreturn]]
  25. no excepto el especificador
  26. no excepto el operador.
  27. Características del C99:
    • tipos integrales extendidos
    • concatenación de cuerdas estrechas / anchas
    • _ _ STDC_HOSTED _ _
    • _Pragma (X)
    • macros vararg y argumentos de macro vacíos
  28. _ _ func _ _
  29. Espacios de nombres en línea
  30. Delegar constructores
  31. Inicializadores de miembros en clase
  32. predeterminado y eliminar
  33. Operadores de conversión explícitos
  34. Literales definidos por el usuario
  35. Plantillas externas
  36. Argumentos de plantilla predeterminados para plantillas de funciones
  37. Heredar constructores
  38. anulación y final
  39. Regla SFINAE más simple y más general
  40. Modelo de memoria
  41. hilo_local

Componentes de biblioteca estándar

  1. initializer_list para contenedores
  2. Mover semántica para contenedores
  3. lista_delantera
  4. Hash contenedores
    • mapa_desordenado
    • unordered_multimap
    • conjunto_ordenados
    • Desordenado_multiset
  5. Punteros de gestión de recursos
    • unique_ptr
    • shared_ptr
    • débil_ptr
  6. Soporte de concurrencia
    • hilo
    • mutexes
    • Cerraduras
    • variables de condición
  7. Soporte de concurrencia de nivel superior
    • packaged_thread
    • futuro
    • promesa
    • asíncrono
  8. tuplas
  9. regex
  10. Números al azar
    • uniforme_int_distribución
    • distribución normal
    • motor_aleatorio
    • etc.
  11. Nombres de tipo entero, como int16_t, uint32_t y int_fast64_t
  12. formación
  13. Copiar y volver a lanzar excepciones
  14. error del sistema
  15. operaciones emplace () para contenedores
  16. funciones constexpr
  17. Uso sistemático de funciones noexcepto
  18. función y enlace
  19. Cadena a conversiones de valor numérico
  20. Asignadores de alcance
  21. Rasgos de tipo
  22. Utilidades de tiempo: duración y time_point
  23. proporción
  24. salida rápida
  25. Más algoritmos, como move (), copy_if () y is_sorted ()
  26. Recolección de basura ABI
  27. atomística

Características en desuso

  1. Generación del constructor de copia y la asignación de copia para una clase con un destructor.
  2. Asignar una cadena literal a un char *.
  3. Especificación de excepción de C ++ 98
    • manipulador_excepcionado
    • set_unexpected
    • get_unexpected
    • inesperado
  4. Objetos de función y funciones asociadas.
  5. auto_ptr
  6. Registrarse
  7. ++ en un bool
  8. exportar
  9. Moldes estilo C

3
Esto no responde la pregunta.
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.