programa - ¿Qué cambios de última hora se introducen en C++11?





como maquina (9)


El significado de la palabra clave auto cambió.

Sé que al menos uno de los cambios en C ++ 11 hará que algunos códigos antiguos dejen de compilar: la introducción del explicit operator bool() en la biblioteca estándar, que reemplaza las instancias antiguas de operator void*() . Por supuesto, el código que se 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 de ruptura?




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

Sí, el cambio del operator void*() const al explicit operator bool() const será un cambio importante, pero solo si se usa de forma incorrecta dentro y fuera de sí mismo. El código de conformidad 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

Edición : solo recordatorio, std::identity<T> se eliminará en C ++ 0x (vea la nota). Es una estructura de conveniencia para hacer tipos dependientes. Dado que la estructura realmente no hace mucho, esto debería arreglarlo:

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



La falla de extracción de la 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

  • Una secuencia de caracteres se ha acumulado 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 en val y ios_base::goodbit se almacena en err .
  • La secuencia de caracteres acumulados en la etapa 2 habría causado que scanf informara un error de entrada. ios_base::failbit se asigna a err . [ed: Nada se almacena en val .]

[C++11: 22.4.2.1.2/3]: [..] El valor numérico que se almacenará puede ser uno de los siguientes:

  • cero, si la función de conversión no logra convertir todo el campo . ios_base::failbit se asigna a err .
  • el valor representable más positivo, si el campo representa un valor positivo demasiado grande para ser representado en val . ios_base::failbit se 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 ser representado en val . ios_base::failbit se asigna a err .
  • el valor convertido, de lo contrario.

El valor numérico resultante se almacena en val .

Implementaciones

  • GCC 4.8 sale correctamente para C ++ 11 :

    Afirmación `x == -1 'falló

  • GCC 4.5-4.8 todos los resultados para C ++ 03 son los siguientes, que parecen ser un error:

    Afirmación `x == -1 'falló

  • Visual C ++ 2008 Express genera correctamente para C ++ 03:

    Afirmación fallida: x == 0

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

    Afirmación fallida: x == 0




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

Resumen, parafraseando el FDIS aquí, para hacerlo (mejor) adecuado como una 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, así que las dejo para que otros las desarrollen.

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 los que se pueden representar por mucho tiempo podrían cambiar de un tipo entero sin signo a un signo largo y largo.

El código válido de C ++ 2003 que utiliza la 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 auto como un especificador de clase de almacenamiento puede no ser válido en C ++ 0x.

Las reducciones de conversiones 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 doble a int es una conversión restringida:

int x[] = { 2.0 };

Las funciones miembro especiales declaradas implícitamente se de fi nen como eliminadas cuando la definición implícita hubiera sido mal formada.

Un programa válido de C ++ 2003 que usa una de estas funciones miembro especiales en un contexto donde no se requiere la definición (por ejemplo, en una expresión que no es potencialmente evaluada) se vuelve mal formado.

Ejemplo por mi:

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

Tal tamaño de trucos ha sido usado por algunos SFINAE, y necesita ser cambiado ahora :)

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

Ejemplo por mi:

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

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

Las llamadas de este código terminate en C ++ 0x, pero no en C ++ 03. Debido a que la especificación de excepción implícita de A::~A en C ++ 0x es noexcept(true) .

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

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

En C ++ 03, >> siempre sería el token de operador de cambio.

Permitir llamadas dependientes de funciones con enlace interno.

Ejemplo por mi:

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 creación de instancias todavía solo considera declaraciones de vinculación externas).

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); }

No se toma la mejor concordancia f(A) , porque no tiene enlace externo.

Cambios de biblioteca

El código válido de C ++ 2003 que usa cualquier identificador agregado a la biblioteca estándar de C ++ de C ++ 0x puede no compilar o producir resultados diferentes en esta Norma Internacional.

El código válido de C ++ 2003 que #includes encabezados con los nombres de los nuevos encabezados de biblioteca estándar de C ++ 0x puede no ser válido en esta Norma Internacional.

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

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

El código válido de C ++ 2003 que define la override , final , carries_dependency o noreturn como macros no es válido en C ++ 0x.







Rompiendo el cambio?

Bueno, por un lado, si usó decltype , constexpr , nullptr , etc. como identificadores, entonces podría estar en problemas ...




struct x {
   x(int) {}
};

void f(auto x = 3) { }

int main() {
   f();
}

C ++ 03: válido.

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







Una de las mejores explicaciones de la lambda expression es la del autor de C ++ Bjarne Stroustrup en su libro ***The C++ Programming Language*** capítulo 11 ( ISBN-13: 978-0321563842 ):

What is a lambda expression?

Una expresión lambda , a veces también conocida como una función lambda o (estrictamente hablando de manera incorrecta, pero coloquial) como una lambda , es una notación simplificada para definir y utilizar un objeto de función anónimo . En lugar de definir una clase nombrada con un operador (), luego hacer un objeto de esa clase y finalmente invocarlo, podemos usar una taquigrafía.

When would I use one?

Esto es particularmente útil cuando queremos pasar una operación como un argumento a un algoritmo. En el contexto de las interfaces gráficas de usuario (y en otros lugares), tales operaciones a menudo se denominan devoluciones de llamada .

What class of problem do they solve that wasn't possible prior to their introduction?

Aquí supongo que cada acción realizada con la expresión lambda se puede resolver sin ellos, pero con mucho más código y una complejidad mucho mayor. Expresión Lambda: esta es la forma de optimización de su código y una forma de hacerlo más atractivo. Como triste por Stroustup:

formas efectivas de optimización

Some examples

a través de la expresión lambda

void print_modulo(const vector<int>& v, ostream& os, int m) // output v[i] to os if v[i]%m==0
{
    for_each(begin(v),end(v),
        [&os,m](int x) { 
           if (x%m==0) os << x << '\n';
         });
}

o vía función

class Modulo_print {
         ostream& os; // members to hold the capture list int m;
     public:
         Modulo_print(ostream& s, int mm) :os(s), m(mm) {} 
         void operator()(int x) const
           { 
             if (x%m==0) os << x << '\n'; 
           }
};

o incluso

void print_modulo(const vector<int>& v, ostream& os, int m) 
     // output v[i] to os if v[i]%m==0
{
    class Modulo_print {
        ostream& os; // members to hold the capture list
        int m; 
        public:
           Modulo_print (ostream& s, int mm) :os(s), m(mm) {}
           void operator()(int x) const
           { 
               if (x%m==0) os << x << '\n';
           }
     };
     for_each(begin(v),end(v),Modulo_print{os,m}); 
}

Si necesitas, puedes nombrar la lambda expression como a continuación:

void print_modulo(const vector<int>& v, ostream& os, int m)
    // output v[i] to os if v[i]%m==0
{
      auto Modulo_print = [&os,m] (int x) { if (x%m==0) os << x << '\n'; };
      for_each(begin(v),end(v),Modulo_print);
 }

O supongamos otra muestra simple.

void TestFunctions::simpleLambda() {
    bool sensitive = true;
    std::vector<int> v = std::vector<int>({1,33,3,4,5,6,7});

    sort(v.begin(),v.end(),
         [sensitive](int x, int y) {
             printf("\n%i\n",  x < y);
             return sensitive ? x < y : abs(x) < abs(y);
         });


    printf("sorted");
    for_each(v.begin(), v.end(),
             [](int x) {
                 printf("x - %i;", x);
             }
             );
}

generará siguiente

0

1

0

1

0

1

0

1

0

1

0 sortedx - 1; x - 3; x - 4; x - 5; x - 6; x - 7; x - 33;

[] - esta es la lista de captura o lambda introducer : si las lambdas no requieren acceso a su entorno local, podemos usarla.

Cita del libro:

El primer carácter de una expresión lambda es siempre [ . Un introductor lambda puede tomar varias formas:

[] : una lista de captura vacía. Esto implica que no se pueden utilizar nombres locales del contexto circundante en el cuerpo lambda. Para tales expresiones lambda, los datos se obtienen de argumentos o de variables no locales.

[&] : captura implícitamente por referencia. Se pueden utilizar todos los nombres locales. Se accede a todas las variables locales por referencia.

[=] : captura implícitamente por valor. Se pueden utilizar todos los nombres locales. Todos los nombres se refieren a copias de las variables locales tomadas en el punto de llamada de la expresión lambda.

[lista de captura]: captura explícita; la lista de captura es la lista de nombres de variables locales que se capturarán (es decir, se almacenarán en el objeto) por referencia o por valor. Las variables con nombres precedidos por & se capturan por referencia. Otras variables son capturadas por valor. Una lista de captura también puede contener esto y los nombres seguidos de ... como elementos.

[&, lista de captura] : captura implícitamente por referencia todas las variables locales con nombres no mencionados en la lista. La lista de captura puede contener esto. Los nombres listados no pueden ir precedidos por &. Las variables nombradas en la lista de captura se capturan por valor.

[=, lista de captura] : captura implícitamente por valor todas las variables locales con nombres no mencionados en la lista. La lista de captura no puede contener esto. Los nombres listados deben ir precedidos por &. Las variables nombradas en la lista de captura se capturan por referencia.

Tenga en cuenta que un nombre local precedido por & siempre se captura por referencia y un nombre local no precedido por & siempre se captura por valor. Solo la captura por referencia permite la modificación de variables en el entorno de llamada.

Additional

Formato de Lambda expression

Referencias adicionales:





c++ c++11