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:
int main()
{
int count = 0 ;
int arrInt[2] = { count++, count++ } ;
return 0 ;
}
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 ):
warning: multiple unsequenced modifications to 'count' [-Wunsequenced]
int arrInt[2] = { count++, count++ } ;
^ ~~
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:
int i = 0 ;
i = ++ i + 1;
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:
int i = 0 ;
++++i ;
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:
El especificador constexpr declara que es posible evaluar el valor de la función o variable en tiempo de compilación. Dichas variables y funciones se pueden usar donde solo se permiten expresiones constantes de tiempo de compilación.
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:
#include <limits>
int main()
{
int x[std::numeric_limits<unsigned int>::min()+2] ;
}
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:
warning: variable length arrays are a C99 feature [-Wvla-extension]
int x[std::numeric_limits<unsigned int>::min()+2] ;
^
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:
#include <iostream>
#include <stdexcept>
struct S
{
~S() { throw std::runtime_error(""); } // bad, but acceptable
};
int main()
{
try { S s; }
catch (...) {
std::cerr << "exception occurred";
}
std::cout << "success";
}
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:
#include <iostream>
#include <vector>
#include <algorithm>
class Comparators
{
public:
bool operator()(int first, int second)
{
return first < second;
}
};
int main()
{
class ComparatorsInner : public Comparators{};
std::vector<int> compares ;
compares.push_back(20) ;
compares.push_back(10) ;
compares.push_back(30) ;
ComparatorsInner comparatorInner;
std::sort(compares.begin(), compares.end(), comparatorInner);
std::vector<int>::iterator it;
for(it = compares.begin(); it != compares.end(); ++it)
{
std::cout << (*it) << std::endl;
}
}
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 :
#include <iostream>
template<int I> struct X {
static int const c = 2;
};
template<> struct X<0> {
typedef int c;
};
template<typename T> struct Y {
static int const c = 3;
};
static int const c = 4;
int main() {
std::cout << (Y<X<1> >::c >::c>::c) << '\n';
std::cout << (Y<X< 1>>::c >::c>::c) << '\n';
}
y el resultado en C ++ 03 es:
0
3
y en C ++ 11:
0
0
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 :
std::vector<T> test(1);
produce resultados diferentes en C ++ 03 y C ++ 11:
#include <iostream>
#include <vector>
struct T
{
bool flag;
T() : flag(false) {}
T(const T&) : flag(true) {}
};
int main()
{
std::vector<T> test(1);
bool is_cpp11 = !test[0].flag;
std::cout << is_cpp11 << std::endl ;
}
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:
int x[] = { 2.0 };
Esto está cubierto en el borrador de la sección estándar de C ++ 11 8.5.4
Lista-inicialización párrafo 3 :
La inicialización de lista de un objeto o referencia de tipo T se define de la siguiente manera:
y contiene la siguiente viñeta ( énfasis mío ):
De lo contrario, si T es un tipo de clase, se consideran los constructores. Los constructores aplicables se enumeran y el mejor se elige mediante resolución de sobrecarga (13.3, 13.3.1.7). Si se requiere una conversión de reducción (ver más abajo) para convertir cualquiera de los argumentos, el programa está mal formado
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
#define u8 "abc"
const char *s = u8"def"; // Previously "abcdef", now "def"
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.
#define _x "there"
"hello"_x // #1
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.
auto
podría dar lugar a una situación como esta