¿Es posible que el código C ++ se ajuste tanto al estándar C ++ 03 como al estándar C ++ 11 , pero hace diferentes cosas según el estándar que se está compilando?
c++
c++11
language-lawyer
c++03
Erik Sjölund
fuente
fuente
auto
podría dar lugar a una situación como esta>>
cuando se usa en una plantilla. Puede llegar a una situación en la que pueda compilar para ambos estándares. Otro para el que estoy seguro sería fácil encontrar cambios es en la inicialización.auto
pueda causar esto. Con el significado anterior, unaauto
declaración requiere un nombre de tipo; con el nuevo significado, no se permite un nombre de tipo.Respuestas:
La respuesta es un sí definitivo. En el lado positivo hay:
En el lado negativo, se enumeran varios ejemplos en el apéndice C de la norma. Aunque hay muchos más negativos que positivos, es mucho menos probable que ocurra cada uno de ellos.
Literales de cadena
y
Escriba conversiones de 0
En C ++ 11, solo los literales son constantes de puntero entero nulo:
Resultados redondeados después de la división entera y módulo
En C ++ 03, se permitió al compilador redondear hacia 0 o hacia infinito negativo. En C ++ 11 es obligatorio redondear hacia 0
Espacios en blanco entre llaves de cierre de plantilla anidadas >> vs>>
Dentro de una especialización o instanciación
>>
, en su lugar, se podría interpretar como un desplazamiento a la derecha en C ++ 03. Sin embargo, es más probable que esto rompa el código existente: (de http://gustedt.wordpress.com/2013/12/15/a-disimprovement-observed-from-the-outside-right-angle-brackets/ )El operador
new
ahora puede lanzar otras excepciones questd::bad_alloc
Los destructores declarados por el usuario tienen un ejemplo de especificación de excepción implícita de ¿Qué cambios importantes se introducen en C ++ 11?
size()
de contenedores ahora se requiere para ejecutarse en O (1)std::ios_base::failure
no deriva directamente destd::exception
másMientras que la clase base directa es nueva,
std::runtime_error
no lo es. Así:fuente
noexecpt(true)
por lo quethrow
en un destructor ahora se llamarástd::terminate
. ¡Pero espero que cualquiera que haya escrito dicho código esté contento con esto!catch (std::exception &)
aún se capturastd::ios_base::failure
.operator new
es preciso (ahora puede arrojarstd::bad_array_new_length
), pero tu ejemplo no muestra eso en absoluto. El código que muestra es el mismo en C ++ 03 y C ++ 11 AFAIK.Le indico este artículo y el seguimiento , que tiene un buen ejemplo de cómo
>>
puede cambiar el significado de C ++ 03 a C ++ 11 sin dejar de compilar ambos.La parte clave es la línea
main
, que es una expresión.En C ++ 03:
En C ++ 11
Felicidades, dos resultados diferentes para la misma expresión. De acuerdo, el C ++ 03 apareció con un formulario de advertencia Clang cuando lo probé.
fuente
typename
de::two
en C ++ versión 03true
ofalse
para los diferentes estándares. Tal vez podríamos usarlo como una prueba de función </joke>warning: comparisons like ‘X<=Y<=Z’ do not have their mathematical meaning [-Wparentheses]
), pero sigue siendo un buen ejemplo de cómo el::
operador ambiguo cambia de significado (ya sea refiriéndose al alcance global o desreferenciando al que está justo delante de él)Sí, hay varios cambios que harán que el mismo código dé como resultado un comportamiento diferente entre C ++ 03 y C ++ 11. Las diferencias en las reglas de secuencia hacen algunos cambios interesantes que incluyen un comportamiento previamente indefinido que se vuelve bien definido.
1. mutaciones múltiples de la misma variable dentro de una lista de inicializador
Un caso de esquina muy interesante sería múltiples mutaciones de la misma variable dentro de una lista de inicializadores, por ejemplo:
Tanto en C ++ 03 como en C ++ 11 esto está bien definido, pero el orden de evaluación en C ++ 03 no está especificado, pero en C ++ 11 se evalúan en el orden en que aparecen . Entonces, si compilamos usando el
clang
modo C ++ 03, nos da la siguiente advertencia ( verlo en vivo ):pero no proporciona una advertencia en C ++ 11 ( verlo en vivo ).
2. Las nuevas reglas de secuenciación hacen que i = ++ i + 1; bien definido en C ++ 11
Las nuevas reglas de secuencia adoptadas después de C ++ 03 significan que:
ya no es un comportamiento indefinido en C ++ 11, esto está cubierto en el informe de defectos 637. Las reglas de secuencia y el ejemplo no están de acuerdo
3. Las nuevas reglas de secuencia también hacen que ++++ i; bien definido en C ++ 11
Las nuevas reglas de secuencia adoptadas después de C ++ 03 significan que:
ya no es un comportamiento indefinido en C ++ 11.
4. Desplazamientos a la izquierda firmados ligeramente más sensibles
Los borradores posteriores de C ++ 11 incluyen
N3485
el enlace a continuación que corrigió el comportamiento indefinido de cambiar un bit dentro o más allá del bit de signo . Esto también está cubierto en el informe de defectos 1457 . Howard Hinnant comentó sobre la importancia de este cambio en el hilo de ¿Es el desplazamiento a la izquierda (<<) un comportamiento entero indefinido negativo en C ++ 11? .5. las funciones constexpr pueden tratarse como expresiones constantes de tiempo de compilación en C ++ 11
C ++ 11 introdujo funciones constexpr que:
Si bien C ++ 03 no tiene la función constexpr , no tenemos que usar explícitamente la palabra clave constexpr ya que la biblioteca estándar proporciona muchas funciones en C ++ 11 como constexpr . Por ejemplo std :: numeric_limits :: min . Lo que puede conducir a un comportamiento diferente, por ejemplo:
El uso
clang
en C ++ 03 haráx
que sea una matriz de longitud variable, que es una extensión y generará la siguiente advertencia:mientras que en C ++ 11
std::numeric_limits<unsigned int>::min()+2
es una expresión constante de tiempo de compilación y no requiere la extensión VLA.6. En C ++ 11 las excepciones de excepción se generan implícitamente para sus destructores
Dado que en C ++ 11 el destructor definido por el usuario tiene una
noexcept(true)
especificación implícita como se explica en los destructores noexcept , significa que el siguiente programa:En C ++ 11 llamará
std::terminate
pero se ejecutará con éxito en C ++ 03.7. En C ++ 03, los argumentos de plantilla no pueden tener un enlace interno
Esto se cubre muy bien en Por qué std :: sort no acepta Comparar clases declaradas dentro de una función . Por lo tanto, el siguiente código no debería funcionar en C ++ 03:
pero actualmente
clang
permite este código en modo C ++ 03 con una advertencia, a menos que use la-pedantic-errors
marca, lo cual es un poco asqueroso, véalo en vivo .8. >> ya no está mal formado al cerrar varias plantillas
El uso
>>
para cerrar varias plantillas ya no está mal formado, pero puede generar código con resultados diferentes en C ++ 03 y C + 11. El siguiente ejemplo se toma de los corchetes de ángulo recto y la compatibilidad con versiones anteriores :y el resultado en C ++ 03 es:
y en C ++ 11:
9. C ++ 11 cambia algunos de los constructores std :: vector
El código ligeramente modificado de esta respuesta muestra que usando el siguiente constructor de std :: vector :
produce resultados diferentes en C ++ 03 y C ++ 11:
10. Reducción de conversiones en inicializadores agregados
En C ++ 11, una conversión de reducción en los inicializadores agregados está mal formada y parece que
gcc
permite esto tanto en C ++ 11 como en C ++ 03, aunque proporciona una advertencia por defecto en C ++ 11:Esto está cubierto en el borrador de la sección estándar de C ++ 11
8.5.4
Lista-inicialización párrafo 3 :y contiene la siguiente viñeta ( énfasis mío ):
Esta y muchas más instancias están cubiertas en el borrador de la sección estándar
annex C.2
C ++ C ++ e ISO C ++ 2003 . También incluye:Nuevos tipos de literales de cadena [...] Específicamente, las macros denominadas R, u8, u8R, u, uR, U, UR o LR no se expandirán cuando estén adyacentes a un literal de cadena, sino que se interpretarán como parte del literal de cadena. . Por ejemplo
Soporte de cadena literal definido por el usuario [...] Anteriormente, el # 1 habría consistido en dos tokens de preprocesamiento separados y la macro _x se habría expandido. En esta Norma Internacional, el # 1 consiste en un solo token de preprocesamiento, por lo que la macro no se expande.
Especifique el redondeo para resultados de código entero /% [...] 2003 que usa división entera redondea el resultado hacia 0 o hacia infinito negativo, mientras que esta Norma Internacional siempre redondea el resultado hacia 0.
La complejidad de las funciones miembro de size () ahora es constante [...] Algunas implementaciones de contenedores que cumplen con C ++ 2003 pueden no cumplir con los requisitos de size () especificados en esta Norma Internacional. Ajustar contenedores como std :: list a los requisitos más estrictos puede requerir cambios incompatibles.
Cambiar la clase base de std :: ios_base :: failure [...] std :: ios_base :: failure ya no se deriva directamente de std :: excepción, sino que ahora se deriva de std :: system_error, que a su vez se deriva de std :: runtime_error. El código válido de C ++ 2003 que asume que std :: ios_base :: failure se deriva directamente de std :: excepción puede ejecutarse de manera diferente en este estándar internacional.
fuente
Un cambio potencialmente incompatible con versiones anteriores es peligroso en los constructores de contenedores de secuencia, como
std::vector
, específicamente, en la sobrecarga que especifica el tamaño inicial. Donde en C ++ 03, copiaron un elemento construido por defecto, en C ++ 11 construyeron por defecto cada uno.Considere este ejemplo (utilizando
boost::shared_ptr
para que sea válido C ++ 03):C ++ 03 Ejemplo en vivo
Ejemplo de C ++ 11 Live
La razón es que C ++ 03 especificó una sobrecarga para "especificar el tamaño y el elemento prototipo" y "especificar solo el tamaño", de esta manera (los argumentos del asignador se omiten por brevedad):
Esto siempre se copiará
prototype
en lossize
tiempos del contenedor . Cuando se llama con un solo argumento, por lo tanto, crearásize
copias de un elemento construido por defecto.En C ++ 11, esta firma del constructor se eliminó y se reemplazó con estas dos sobrecargas:
El segundo funciona como antes, creando
size
copias delprototype
elemento. Sin embargo, el primero (que ahora maneja llamadas con solo el argumento de tamaño especificado) construye por defecto cada elemento individualmente.Supongo que la razón de este cambio es que la sobrecarga de C ++ 03 no sería utilizable con un tipo de elemento de solo movimiento. Sin embargo, es un cambio innovador, y rara vez se documenta en eso.
fuente
deque
tener diez widgets separados, no diez widgets que compartan el mismo recurso.El resultado de una lectura fallida de un
std::istream
ha cambiado. CppReference lo resume muy bien:Esto es principalmente un problema si estás acostumbrado a la nueva semántica y luego tienes que escribir usando C ++ 03. La siguiente no es una práctica particularmente buena, pero está bien definida en C ++ 11:
Sin embargo, en C ++ 03, el código anterior utiliza una variable no inicializada y, por lo tanto, tiene un comportamiento indefinido.
fuente
int x = 1, y = 1; cin >> x >> y; cout << x*y;
. Con C ++ 03, esto habría producido correctamentex
cuando no sey
pudiera leer.Este subproceso Las diferencias, si las hay, entre C ++ 03 y C ++ 0x que se pueden detectar en tiempo de ejecución tiene ejemplos (copiados de ese subproceso) para determinar las diferencias de idioma, por ejemplo explotando el colapso de referencia de C ++ 11:
y c ++ 11 que permite tipos locales como parámetros de plantilla:
fuente
Aquí hay otro ejemplo:
Huellas dactilares:
Ver el resultado en Coliru
fuente