lenguaje - programa en c++
¿Puede el código de C++ ser válido tanto en C++ 03 como en C++ 11 pero hacer cosas diferentes? (7)
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
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 elfailbit
. (hasta C ++ 11)Si la extracción falla, cero se escribe a
value
y se establecefailbit
. Si la extracción resulta en el valor demasiado grande o demasiado pequeño para ajustarse alvalue
, sestd::numeric_limits<T>::max()
ostd::numeric_limits<T>::min()
y sefailbit
indicador defailbit
. (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.
Este hilo Qué diferencias, si las hay, entre C ++ 03 y C ++ 0x pueden detectarse en tiempo de ejecución tiene ejemplos (copiados de ese hilo) para determinar las diferencias de idioma, por ejemplo, aprovechando el colapso de referencia de C ++ 11:
template <class T> bool f(T&) {return true; }
template <class T> bool f(...){return false;}
bool isCpp11()
{
int v = 1;
return f<int&>(v);
}
y c ++ 11 permitiendo tipos locales como parámetros de plantilla:
template <class T> bool cpp11(T) {return true;} //T cannot be a local type in C++03
bool cpp11(...){return false;}
bool isCpp0x()
{
struct local {} var; //variable with local type
return cpp11(var);
}
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";
}
Le señalo gustedt.wordpress.com/2013/12/15/… y el seguimiento , que tiene un buen ejemplo de cómo >>
puede cambiar el significado de C ++ 03 a C ++ 11 mientras compila ambos.
bool const one = true;
int const two = 2;
int const three = 3;
template<int> struct fun {
typedef int two;
};
template<class T> struct fon {
static int const three = ::three;
static bool const one = ::one;
};
int main(void) {
fon< fun< 1 >>::three >::two >::one; // valid for both
}
La parte clave es la línea en main
, que es una expresión.
En C ++ 03:
1 >> ::three = 0
=> fon< fun< 0 >::two >::one;
fun< 0 >::two = int
=> fon< int >::one
fon< int >::one = true
=> true
En C ++ 11
fun< 1 > is a type argument to fon
fon< fun<1> >::three = 3
=> 3 > ::two > ::one
::two is 2 and ::one is 1
=> 3 > 2 > 1
=> (3 > 2) > 1
=> true > 1
=> 1 > 1
=> false
Felicitaciones, dos resultados diferentes para la misma expresión. Por supuesto, el C ++ 03 apareció con una forma de advertencia Clang cuando lo probé.
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.
Un cambio potencialmente peligroso, incompatible con versiones anteriores, es 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 (usando boost::shared_ptr
para que sea válido C ++ 03):
#include <deque>
#include <iostream>
#include "boost/shared_ptr.hpp"
struct Widget
{
boost::shared_ptr<int> p;
Widget() : p(new int(42)) {}
};
int main()
{
std::deque<Widget> d(10);
for (size_t i = 0; i < d.size(); ++i)
std::cout << "d[" << i << "] : " << d[i].p.use_count() << ''/n'';
}
La razón es que C ++ 03 especificó una sobrecarga para "especificar tamaño y elemento de prototipo" y "especificar solo tamaño", como este (los argumentos del asignador se omiten por brevedad):
container(size_type size, const value_type &prototype = value_type());
Esto siempre copiará prototype
en los tiempos de size
contenedor. Cuando se le llama con un solo argumento, por lo tanto, creará copias de size
de un elemento construido por defecto.
En C ++ 11, esta firma del constructor se eliminó y se reemplazó con estas dos sobrecargas:
container(size_type size);
container(size_type size, const value_type &prototype);
El segundo funciona como antes, creando copias de size
del elemento prototype
. Sin embargo, el primero (que ahora maneja las llamadas con solo el argumento de tamaño especificado) construye cada elemento de manera individual.
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. Pero no obstante, es un cambio importante, y rara vez se documenta en eso.