c++ codigos - ¿Puede el código de C ++ ser válido tanto en C ++ 03 como en C ++ 11 pero hacer cosas diferentes?




4 Answers

La respuesta es un sí definitivo. En el lado positivo hay:

  • El código que anteriormente los objetos copiados implícitamente ahora los moverá implícitamente cuando sea posible.

En el lado negativo, varios ejemplos se enumeran en el apéndice C del estándar. Si bien hay muchos más negativos que positivos, es mucho menos probable que ocurra cada uno de ellos.

Literales de cuerda

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

y

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

Escriba conversiones de 0

En C ++ 11, solo los literales son constantes de puntero nulo enteras:

void f(void *); // #1
void f(...); // #2
template<int N> void g() {
    f(0*N); // Calls #2; used to call #1
}

Resultados redondeados después de división entera y módulo

En C ++ 03 se permitió que el compilador redondeara hacia 0 o hacia el infinito negativo. En C ++ 11 es obligatorio redondear hacia 0.

int i = (-1) / 2; // Might have been -1 in C++03, is now ensured to be 0

Espacios en blanco entre llaves anidadas de cierre de plantilla >> vs>>

Dentro de una especialización o creación de instancias, la >> podría interpretarse como un cambio a la derecha en C ++ 03. Sin embargo, es más probable que esto rompa el código existente: (de gustedt.wordpress.com/2013/12/15/… )

template< unsigned len > unsigned int fun(unsigned int x);
typedef unsigned int (*fun_t)(unsigned int);
template< fun_t f > unsigned int fon(unsigned int x);

void total(void) {
    // fon<fun<9> >(1) >> 2 in both standards
    unsigned int A = fon< fun< 9 > >(1) >>(2);
    // fon<fun<4> >(2) in C++03
    // Compile time error in C++11
    unsigned int B = fon< fun< 9 >>(1) > >(2);
}

El operador new ahora puede lanzar otras excepciones que std::bad_alloc

struct foo { void *operator new(size_t x){ throw std::exception(); } }
try {
    foo *f = new foo();
} catch (std::bad_alloc &) {
    // c++03 code
} catch (std::exception &) {
    // c++11 code
}

Los destructores declarados por el usuario tienen un ejemplo de especificación de excepción implícita de ¿Qué cambios de última hora se introducen en C ++ 11?

struct A {
    ~A() { throw "foo"; } // Calls std::terminate in C++11
};
//...
try { 
    A a; 
} catch(...) { 
    // C++03 will catch the exception
} 

size() de los contenedores ahora se requiere para ejecutarse en O (1)

std::list<double> list;
// ...
size_t s = list.size(); // Might be an O(n) operation in C++03

std::ios_base::failure ya no se deriva directamente de la std::exception

Mientras que la clase base directa es nueva, std::runtime_error no lo es. Así:

try {
    std::cin >> variable; // exceptions enabled, and error here
} catch(std::runtime_error &) {
    std::cerr << "C++11\n";
} catch(std::ios_base::failure &) {
    std::cerr << "Pre-C++11\n";
}
sus funciones

¿Es posible que el código C ++ se ajuste tanto al estándar C++03 estándar C++11 , pero hacen diferentes cosas dependiendo de bajo qué estándar se está compilando?




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 que algunos cambios interesantes incluyan un comportamiento previamente indefinido que se vuelva bien definido.

1. mutaciones múltiples de la misma variable dentro de una lista de inicializadores

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 el uso de clang en modo C ++ 03, proporcionamos la siguiente advertencia ( véalo en vivo ):

warning: multiple unsequenced modifications to 'count' [-Wunsequenced]

    int arrInt[2] = { count++, count++ } ;

                           ^        ~~

pero no proporciona una advertencia en C ++ 11 ( véalo en vivo ).

2. Las nuevas reglas de secuencia hacen que i = ++ i + 1; bien definido en C ++ 11

Las nuevas reglas de secuenciación adoptadas después de C ++ 03 significa que:

int i = 0 ;
i = ++ i + 1;

Ya no existe un comportamiento indefinido en C ++ 11, esto está cubierto en el informe de defectos 637. Las reglas de secuenciación y el ejemplo no están de acuerdo

3. Las nuevas reglas de secuenciación también hacen ++++ i; bien definido en C ++ 11

Las nuevas reglas de secuenciación adoptadas después de C ++ 03 significa que:

int i = 0 ;
++++i ;

Ya no es un comportamiento indefinido en C ++ 11.

4. Turnos a la izquierda ligeramente más sensatos y firmados

Los borradores posteriores de C ++ 11 incluyen N3485 que, a continuación, vinculo, corrigió el comportamiento indefinido de cambiar un bit 1 dentro o más allá del bit de signo . Esto también está cubierto en el informe de defectos 1457 . Howard Hinnant comentó sobre el significado de este cambio en el hilo en ¿Es el desplazamiento a la izquierda (<<) un entero indefinido negativo comportamiento 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 pueden usarse cuando solo se permiten expresiones de constantes de tiempo de compilación.

mientras que C ++ 03 no tiene la característica 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 llevar a diferentes comportamientos, por ejemplo:

#include <limits>

int main()
{
    int x[std::numeric_limits<unsigned int>::min()+2] ;
}

Al usar clang en C ++ 03, esto hará que x 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 compilación y no requiere la extensión VLA.

6. En C ++ 11, las excepciones de excepción excepto se generan implícitamente para sus destructores

Dado que en C ++ 11 el destructor definido por el usuario tiene una noexcept(true) implícita de noexcept(true) como se explica en noexcept destructors , 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á a std::terminate pero se ejecutará correctamente en C ++ 03.

7. En C ++ 03, los argumentos de la plantilla no pueden tener vinculación interna

Esto se explica muy bien en ¿Por qué std :: sort no acepta clases de comparación declaradas dentro de una función ? Entonces 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, el clang permite este código en el modo C ++ 03 con una advertencia a menos que use la -pedantic-errors , que es un poco -pedantic-errors en vivo .

8. >> ya no está mal formado al cerrar varias plantillas

El uso de >> para cerrar varias plantillas ya no está mal formado, pero puede llevar a código con resultados diferentes en C ++ 03 y C + 11. El siguiente ejemplo está tomado de los soportes 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 de vectores std ::

El código ligeramente modificado de esta respuesta muestra que al usar el siguiente constructor de std::vector :

std::vector<T> test(1);

Produce diferentes resultados 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 las conversiones en inicializadores agregados.

En C ++ 11, una conversión restringida 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 de forma predeterminada en C ++ 11:

int x[] = { 2.0 };

Esto se trata en el párrafo 3 de la sección 8.5.4 del borrador de la norma C ++ 11 : inicialización de lista, 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, los constructores son considerados. Los constructores correspondientes 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 de C ++ , annex C.2 C ++ e ISO C ++ 2003 . También incluye:

  • Nuevos tipos de literales de cadena [...] Específicamente, las macros llamadas 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"
    
  • El 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 consta de un solo tokens de preprocesamiento, por lo que la macro no se expande.

    #define _x "there"
    "hello"_x // #1
    
  • Especifique el redondeo para los resultados del código entero / y% [...] 2003 que usa la división entera redondea el resultado hacia 0 o hacia el infinito negativo, mientras que esta Norma Internacional siempre redondea el resultado hacia 0.

  • La complejidad de las funciones de miembro de tamaño () ahora es constante [...] Algunas implementaciones de contenedores que cumplen con C ++ 2003 pueden no cumplir con los requisitos de tamaño () 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 :: exception, 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 :: exception puede ejecutarse de manera diferente en esta Norma Internacional.




El resultado de una lectura fallida de un std::istream ha cambiado. CppReference lo resume muy bien:

Si la extracción falla (por ejemplo, si se ingresó una letra donde se espera un dígito), el value se deja sin modificar y se establece el failbit . (hasta C ++ 11)

Si la extracción falla, cero se escribe a value y se establece failbit . Si la extracción resulta en el valor demasiado grande o demasiado pequeño para ajustarse al value , se std::numeric_limits<T>::max() o std::numeric_limits<T>::min() y se failbit indicador de failbit . (desde C ++ 11)

Esto es principalmente un problema si está acostumbrado a la nueva semántica y luego tiene que escribir utilizando C ++ 03. Lo siguiente no es una buena práctica en particular, pero está bien definido en C ++ 11:

int x, y;
std::cin >> x >> y;
std::cout << x + y;

Sin embargo, en C ++ 03, el código anterior utiliza una variable no inicializada y, por lo tanto, tiene un comportamiento indefinido.




Aquí hay otro ejemplo:

#include <iostream>

template<class T>
struct has {
  typedef char yes;
  typedef yes (&no)[2];    
  template<int> struct foo;    
  template<class U> static yes test(foo<U::bar>*);      
  template<class U> static no  test(...);    
  static bool const value = sizeof(test<T>(0)) == sizeof(yes);
};

enum foo { bar };

int main()
{
    std::cout << (has<foo>::value ? "yes" : "no") << std::endl;
}

Huellas dactilares:

Using c++03: no
Using c++11: yes

Ver el resultado en Coliru.




Related