En este artículo, se describen todos los cambios importantes de Visual Studio 2015 desde Visual Studio 2003 y, en este artículo, los términos “nuevo comportamiento” o “ahora” hacen referencia a Visual Studio 2015 y versiones posteriores. Los términos “comportamiento anterior” y “antes” hacen referencia a Visual Studio 2013 y versiones anteriores.
Al actualizar a una nueva versión de Visual Studio, se pueden producir errores de compilación o en tiempo de ejecución en código previamente compilado que se ejecutaba correctamente. Los cambios en la nueva versión que producen tales problemas se conocen como cambios importantesy normalmente son necesarios debido a las modificaciones en el estándar del lenguaje C++, las firmas de función o la disposición de los objetos en la memoria.
Para evitar errores en tiempo de ejecución que son difíciles de detectar y diagnosticar, se recomienda que no vincule nunca de forma estática a binarios compilados con otra versión del compilador. Además, cuando actualice un proyecto EXE o DLL, asegúrese de actualizar las bibliotecas a las que está vinculado. No pase tipos CRT (Runtime de C) o de la biblioteca estándar de C++ entre archivos binarios, incluidos los archivos DLL, compilados con otras versiones del compilador. Para obtener más información, vea Potential Errors Passing CRT Objects Across DLL Boundaries.
Se recomienda que nunca escriba código que dependa de una disposición determinada de un objeto que no sea una interfaz COM o un objeto POD. Si escribe código de esta manera, debe asegurarse de que funciona después de la actualización. Para obtener más información, consulte Portabilidad en los límites de ABI (C++ moderno).
Además, las mejoras continuas en la conformidad del compilador a veces pueden cambiar cómo entiende el compilador el código fuente existente. Por ejemplo, es posible que se produzcan errores nuevos o diferentes durante la compilación, o incluso diferencias de comportamiento en el código previamente compilado que parecía ejecutarse de forma correcta. Aunque estas mejoras no son cambios importantes como los descritos en este documento, es posible que tenga que modificar el código fuente para resolver estos problemas:
La familia de funciones printf y scanf se define ahora en línea.
Las definiciones de todas las funciones de printf
y scanf
se han movido insertadas en <stdio.h>
, <conio.h>
y otros encabezados de CRT. Este cambio importante provoca un error del enlazador (LNK2019, símbolo externo sin resolver) en todos los programas en los que se han declarado estas funciones de forma local sin incluir los encabezados de CRT adecuados. Si es posible, debe actualizar el código para incluir los encabezados de CRT (es decir, agregar #include <stdio.h>
) y las funciones insertadas, pero si no desea modificar el código para incluir estos archivos de encabezado, una solución alternativa es agregar legacy_stdio_definitions.lib
a la entrada del enlazador.
Para agregar esta biblioteca a la entrada de vinculador en el IDE, abra el menú contextual del nodo de proyecto y elija Propiedades. A continuación, en el cuadro de diálogo Propiedades del proyecto , seleccione Vinculador, y edite Entrada del vinculador para agregar legacy_stdio_definitions.lib
a la lista separada por signos de punto y coma.
Si el proyecto está vinculado a bibliotecas estáticas compiladas con una versión de Visual Studio anterior a la 2015, el enlazador podría informar de un símbolo externo sin resolver. Estos errores pueden hacer referencia a definiciones internas para _iob
, _iob_func
o importaciones relacionadas para determinadas funciones <stdio.h>
en forma de imp*. Microsoft recomienda que, al actualizar un proyecto, recompile todas las bibliotecas estáticas con la versión más reciente del compilador y las bibliotecas de C++. Si se trata de una biblioteca de terceros cuyo origen no está disponible, solicite al autor un binario actualizado o encapsule el uso de esa biblioteca en un archivo DLL independiente que luego pueda compilar con la versión anterior del compilador y las bibliotecas.
Advertencia
Si se vincula con el SDK de Windows 8.1 o con versiones anteriores, es posible encontrar estos errores de símbolo externo sin resolver. En ese caso, debe resolver el error agregando legacy_stdio_definitions.lib a la entrada de vinculador tal como se describió anteriormente.
Para solucionar errores de símbolos sin resolver, puede intentar usar dumpbin.exe
para examinar los símbolos definidos en un binario. Pruebe la siguiente línea de comandos para ver los símbolos definidos en una biblioteca.
dumpbin.exe /LINKERMEMBER somelibrary.lib
gets y _getws
Las funciones gets y _getws se han quitado. La función gets se ha quitado de la biblioteca estándar de C en C11 porque su uso no es seguro. La función _getws era una extensión de Microsoft que equivalía a gets, pero para cadenas de caracteres anchos. Como alternativa a estas funciones, puede usar fgets, fgetws, gets_s y _getws_s.
_cgets y _cgetws
Las funciones _cgets y _cgetws se han quitado. Como alternativa a estas funciones, puede usar _cgets_s y _cgetws_s.
Formato de Infinity y NaN
En versiones anteriores, los valores infinitos y NaN adoptaban el formato mediante un conjunto de cadenas centinela específicas de MSVC.
Es posible que cualquiera de estos formatos se haya prefijado con un signo y tenga un formato ligeramente distinto según el ancho de campo y la precisión (a veces con efectos inusuales, por ejemplo, printf("%.2f\n", INFINITY)
imprimía 1.#J porque #INF se "redondeaba" a una precisión de dos dígitos). C99 introdujo nuevos requisitos en el formato de los valores infinitos y NaN. La implementación de MSVC ahora cumple estos requisitos. Las nuevas cadenas son las siguientes:
Cualquiera de ellas puede ir precedida por un signo. Si se usa un especificador de formato en mayúscula (%F en lugar de %f), las cadenas se imprimen en mayúscula (INF
en lugar de inf
), según sea necesario.
Las funciones scanf se han modificado para analizar estas nuevas cadenas de manera que ahora realizan un recorrido de ida y vuelta a través de printf
y scanf
.
Formato y análisis de punto flotante
Se han introducido nuevos algoritmos de análisis y formato de punto flotante para mejorar la corrección. Este cambio afecta a la familias de funciones printf y scanf y funciones como strtod.
Los algoritmos de formato anteriores generaban únicamente un número limitado de dígitos y rellenaban con cero el resto de posiciones decimales. Solían generar cadenas que realizaban un recorrido de ida y vuelta hasta al valor de punto flotante original, pero no eran correctas si se quería el valor exacto (o la representación decimal más cercana al mismo). Los nuevos algoritmos de formato generan tantos dígitos como sea necesario para representar el valor (o para rellenar la precisión especificada). Como ejemplo de la mejora, considere los resultados cuando se imprime una gran potencia de dos:
printf("%.0f\n", pow(2.0, 80))
Salida anterior:
1208925819614629200000000
Nueva salida:
1208925819614629174706176
Los algoritmos de análisis antiguos solo tenían en cuenta un máximo de 17 dígitos significativos de la cadena de entrada y descartaban el resto de los dígitos. Este enfoque es suficiente para generar una aproximación muy precisa del valor representado por la cadena y el resultado se suele acercar mucho al resultado redondeado correctamente. La nueva implementación tiene en cuenta todos los dígitos presentes y genera el resultado redondeado correctamente para todas las entradas (hasta 768 dígitos de longitud). Además, estas funciones ahora respetan el modo de redondeo (que se puede controlar a través de fesetround). Este es un cambio de comportamiento potencialmente importante porque es posible que estas funciones generen otros resultados. Los nuevos resultados siempre son más correctos que los resultados anteriores.
Análisis de punto flotante de hexadecimal e infinity/NaN
Los algoritmos de análisis de punto flotante ahora analizan cadenas hexadecimales de punto flotante (como las generadas por los especificadores de formato de printf %a y %A) y todas las cadenas infinitas y NaN generadas por las funciones printf
, como se ha descrito anteriormente.
Relleno de ceros a la izquierda de %A y %a
Los especificadores de formato %a y %A dan formato a un número de punto flotante como mantisa hexadecimal y exponente binario. En versiones anteriores, las funciones de printf
rellenaban con ceros las cadenas de manera incorrecta. Por ejemplo, printf("%07.0a\n", 1.0)
imprimía 00x1p+0, cuando debería imprimir 0x01p+0. Este error se ha solucionado.
Precisión de %A y %a
La precisión predeterminada de los especificadores de formato %A y %a era de 6 en versiones anteriores de la biblioteca. La precisión predeterminada es ahora de 13, de conformidad con el estándar de C.
Se trata de un cambio de comportamiento en tiempo de ejecución en la salida de cualquier función que usa una cadena de formato con %A o %a. En el comportamiento anterior, la salida que usaba el especificador %A podía ser “1.1A2B3Cp+111”. Ahora, la salida para el mismo valor es “1.1A2B3C4D5E6F7p+111”. Para obtener el comportamiento anterior, puede especificar la precisión, por ejemplo, %.6A. Consulte Especificación de precisión.
Especificador %F
Ahora se admite el especificador de formato/conversión %F. Es funcionalmente equivalente al especificador de formato %f, salvo que en el formato de los valores infinitos y NaN se usan letras mayúsculas.
En versiones anteriores, la implementación solía analizar F y N como modificadores de longitud. Este comportamiento se remontaba a la época de los espacios de direcciones segmentados: estos modificadores de longitud se usaban para indicar punteros far y near, respectivamente, como en %Fp o %Ns. Este comportamiento se ha eliminado. Si se encuentra %F, ahora se trata como el especificador de formato %F; si se encuentra %N, ahora se trata como un parámetro no válido.
Formato de exponente
Los especificadores de formato %e y %E dan formato a un número de punto flotante como mantisa decimal y exponente. En algunos casos, los especificadores de formato %g y %G también dan este formato a los números. En versiones anteriores, CRT generaba siempre las cadenas con exponentes de tres dígitos. Por ejemplo, printf("%e\n", 1.0)
imprimía 1.000000e+000, lo que no era correcto. C exige que si el exponente solo se puede representar mediante uno o dos dígitos, únicamente se imprimirán dos dígitos.
En Visual Studio 2005 se agregó un modificador de conformidad global: _set_output_format. Un programa podía llamar a esta función con el argumento _TWO_DIGIT_EXPONENT, para habilitar la impresión de exponentes conforme a los estándares. El comportamiento predeterminado se ha cambiado al modo de impresión de exponentes conforme a los estándares.
Validación de cadenas de formato
En las versiones anteriores, las funciones printf
y scanf
aceptaban en modo silencioso muchas cadenas de formato no válido, a veces con efectos inesperados. Por ejemplo, %hlhlhld se trataba como %d. Ahora, todas las cadenas de formato no válido se tratan como parámetros no válidos.
Validación de cadenas de modo fopen
En las versiones anteriores, la familia de funciones fopen
aceptaba en modo silencioso algunas cadenas de modo no válido, como r+b+
. Ahora, las cadenas de modo no válido se consideran como parámetros no válidos.
Modo _O_U8TEXT
La función _setmode ahora informa correctamente del modo de flujos abiertos en modo _O_U8TEXT. En versiones anteriores de la biblioteca, informaba de que dichos flujos se abrían en _O_WTEXT.
Se trata de una novedad importante si su código interpreta el modo _O_WTEXT para los flujos en que la codificación es UTF-8. Si su aplicación no es compatible con UTF_8, considere la posibilidad de agregar compatibilidad con esta codificación cada vez más común.
snprintf y vsnprintf
Las funciones snprintf y vsnprintf están ahora implementadas. A menudo, el código anterior proporcionaba versiones de macro de definiciones de estas funciones porque la biblioteca CRT no las implementaba, pero ya no son necesarias en las versiones más recientes. Si snprintf o vsnprintf se definen como una macro antes de incluir <stdio.h>
, la compilación produce un error que indica dónde estaba definida la macro.
Normalmente, la solución a este problema consiste en eliminar todas las declaraciones de snprintf
o vsnprintf
en el código de usuario.
tmpnam genera nombres de archivo utilizables
En versiones anteriores, las funciones tmpnam
y tmpnam_s
generaban nombres de archivo en la raíz de la unidad (por ejemplo, \sd3c.). Estas funciones generan ahora rutas de nombre de archivo utilizables en un directorio temporal.
Encapsulación FILE
En versiones anteriores, el tipo FILE completo se definía públicamente en <stdio.h>
, por lo que era posible que el código de usuario llegara a un FILE y modificara su interior. La biblioteca se ha cambiado para ocultar los detalles de implementación. Como parte de este cambio, FILE tal como se define en <stdio.h>
ahora es un tipo opaco y sus miembros son inaccesibles desde fuera del propio CRT.
_outp e _inp
Las funciones _outp, _outpw, _outpd, _inp, _inpw e _inpd se han quitado.
Para habilitar nuevas optimizaciones y comprobaciones de depuración, la implementación de Visual Studio de la Biblioteca estándar de C++ interrumpe deliberadamente la compatibilidad binaria de una versión a la siguiente. Por consiguiente, cuando se utiliza la Biblioteca estándar de C++, los archivos de objetos y las bibliotecas estáticas que se han compilado con versiones diferentes no se pueden combinar en un binario (EXE o DLL), y los objetos de la Biblioteca estándar de C++ no se pueden pasar entre los archivos binarios que se han compilado con versiones diferentes. Una combinación de este estilo emite errores del vinculador sobre discordancias _MSC_VER. (_MSC_VER es la macro que contiene la versión principal del compilador, por ejemplo, 1800 para Visual Studio 2013). Esta comprobación no detecta la combinación de archivos DLL ni detecta combinaciones que impliquen Visual Studio 2008 o versiones anteriores.
Al actualizar código de versiones anteriores, es posible que también se produzcan errores del compilador causados por las mejoras de conformidad de Visual Studio 2015. Estas mejoras no anulan la compatibilidad binaria de versiones anteriores de Visual Studio, pero puede haber errores del compilador que antes no se producían. Para obtener más información, consulte Visual C++ What's New 2003 through 2015 (Novedades de Visual C++ desde 2003 hasta 2015).
En Visual Studio 2015, las mejoras continuas en la conformidad del compilador a veces pueden cambiar cómo este entiende el código fuente existente. Como resultado, es posible que se produzcan errores nuevos o diferentes durante la compilación, o incluso diferencias de comportamiento en el código previamente compilado que parecía ejecutarse de forma correcta.
Afortunadamente, estas diferencias tienen poco o ningún impacto en la mayoría del código fuente. Cuando se necesita código fuente u otros cambios para resolver estas diferencias, las correcciones suelen ser pequeñas y sencillas. Se han incluido muchos ejemplos de código fuente previamente aceptable que es posible que se tengan que cambiar (antes) y las revisiones para corregirlos (después).
Aunque estas diferencias pueden afectar a su código fuente u otros artefactos de compilación, no afectan a la compatibilidad binaria entre actualizaciones de versiones de Visual Studio. Un cambio importante es más drástico, y puede afectar a la compatibilidad binaria, pero este tipo de alteraciones de compatibilidad binaria solo se producen entre las versiones principales de Visual Studio, por ejemplo entre Visual Studio 2013 y Visual Studio 2015. Para obtener más información sobre los cambios importantes que se han producido entre Visual Studio 2013 y Visual Studio 2015, consulte Cambios de conformidad de Visual Studio 2015.
/Zc:forScope- (opción)
La opción del compilador /Zc:forScope-
está en desuso y se quitará en una próxima versión.
Command line warning D9035: option 'Zc:forScope-' has been deprecated and will be removed in a future release
Normalmente, esta opción se usaba para permitir código no estándar en el que se utilizan variables de bucle después del punto donde, según el estándar, deberían quedar fuera del ámbito. Solo era necesario al compilar con la opción /Za
, ya que sin /Za
, siempre se permite usar una variable de bucle for tras el final del bucle. Si no le interesa el cumplimiento de los estándares (por ejemplo, si su código no está destinado a otros compiladores), puede desactivar la opción /Za
(o establecer la propiedad Deshabilitar extensiones de lenguaje en No). Si le interesa escribir código portable y conforme a los estándares, debe volver a escribir el código de modo que se ajuste a la norma. Para ello, mueva la declaración de dichas variables a un punto fuera del bucle.
int main() {
for (int i = 0; i < 1; i++);
i = 20;
}
Opción del compilador /Zg
La opción de compilador /Zg
(Generar prototipos de función) ya no está disponible. Esta opción del compilador quedó en desuso anteriormente.
Ya no se pueden ejecutar pruebas unitarias con C++ o la CLI desde la línea de comandos con mstest.exe. En su lugar, use vstest.console.exe. Consulte Opciones de la línea de comandos para VSTest.Console.exe.
Palabra clave mutable
El especificador de clase de almacenamiento mutable
ya no se permite en lugares donde anteriormente se compilaba sin errores. Ahora, el compilador produce el error C2071 (clase de almacenamiento no válida). Según el estándar, el especificador mutable
solo se puede aplicar a los nombres de miembros de datos de clase; no se puede aplicar a los nombres declarados como const o static, ni tampoco para hacer referencia a los miembros.
Por ejemplo, suponga el siguiente código:
struct S
{
mutable int &r;
};
Las versiones anteriores del compilador aceptaban esto, pero ahora el compilador produce el siguiente error:
error C2071: 'S::r': illegal storage class
Para corregir el error, quite la palabra clave mutable
redundante.
char_16_t y char32_t
Ya no se puede usar char16_t
o char32_t
como alias en una definición de tipo (typedef
), ya que ahora estos tipos se consideran integrados. Era habitual que los usuarios y creadores de bibliotecas definieran char16_t
y char32_t
como alias de uint16_t
y uint32_t
, respectivamente.
#include <cstdint>
typedef uint16_t char16_t;
typedef uint32_t char32_t;
int main(int argc, char* argv[])
{
uint16_t x = 1; uint32_t y = 2;
char16_t a = x;
char32_t b = y;
return 0;
}
Para actualizar el código, quite las declaraciones typedef
y cambie el nombre de todos los identificadores que estén en conflicto con estos nombres.
Parámetros de plantilla sin tipo
Hay determinado código que implica parámetros de plantilla sin tipo en el que ahora se comprueba la compatibilidad de tipos cuando se proporcionan argumentos de plantilla explícitos. Por ejemplo, el código siguiente se compilaba sin errores en las versiones anteriores de Visual Studio.
struct S1
{
void f(int);
void f(int, int);
};
struct S2
{
template <class C, void (C::*Function)(int) const> void f() {}
};
void f()
{
S2 s2;
s2.f<S1, &S1::f>();
}
El compilador actual produce correctamente un error, porque el tipo de parámetro de plantilla no coincide con el argumento de plantilla (el parámetro es un puntero a miembro const, pero la función f no es const):
error C2893: Failed to specialize function template 'void S2::f(void)'note: With the following template arguments:note: 'C=S1'note: 'Function=S1::f'
Para solucionar este error en el código, asegúrese de que el tipo del argumento de la plantilla que está usando coincide con el tipo declarado del parámetro de plantilla.
__declspec(align)
El compilador ya no acepta __declspec(align)
en las funciones. Esta construcción siempre se ignoraba, pero ahora produce un error del compilador.
error C3323: 'alignas' and '__declspec(align)' are not allowed on function declarations
Para solucionar este problema, quite __declspec(align)
de la declaración de función. Como no tenía ningún efecto, el hecho de quitarlo no cambia nada.
Control de excepciones
Hay un par de cambios en el control de excepciones. En primer lugar, los objetos de excepción deben poderse copiar o mover. El código siguiente se compila en Visual Studio 2013, pero no en Visual Studio 2015:
struct S
{
public:
S();
private:
S(const S &);
};
int main()
{
throw S();
}
El problema es que el constructor de copias es privado, por lo que el objeto no se puede copiar como cuando se controla una excepción de la manera habitual. Lo mismo sucede cuando el constructor de copias se declara explicit
.
struct S
{
S();
explicit S(const S &);
};
int main()
{
throw S();
}
Para actualizar el código, asegúrese de que el constructor de copias correspondiente al objeto de excepción es public
y que no está marcado como explicit
.
Para detectar una excepción por valor, también es necesario que el objeto de excepción se pueda copiar. El código siguiente se compila en Visual Studio 2013, pero no en Visual Studio 2015:
struct B
{
public:
B();
private:
B(const B &);
};
struct D : public B {};
int main()
{
try
{
}
catch (D d)
{
}
}
Para solucionar este problema, cambie el tipo de parámetro de catch
a una referencia.
catch (D& d)
{
}
Literales de cadena seguidos de macros
Ahora el compilador admite literales definidos por el usuario. En consecuencia, los literales de cadena seguidos de macros sin ningún espacio en blanco intermedio se interpretan como literales definidos por el usuario, lo que puede dar lugar a errores o resultados inesperados. Por ejemplo, en los compiladores anteriores el código siguiente se compilaba perfectamente:
#define _x "there"
char* func() {
return "hello"_x;
}
int main()
{
char * p = func();
return 0;
}
El compilador interpretaba este código como un literal de cadena "hello" seguido de una macro, expandida en "there" y, después, los dos literales de cadena se concatenaban en uno solo. En Visual Studio 2015, el compilador interpreta esta secuencia como un literal definido por el usuario, pero como no hay ningún literal _x
que coincida definido por el usuario, produce un error.
error C3688: invalid literal suffix '_x'; literal operator or literal operator template 'operator ""_x' not found
note: Did you forget a space between the string literal and the prefix of the following string literal?
Para solucionar este problema, agregue un espacio entre el literal de cadena y la macro.
Literales de cadena adyacentes
Como en el caso anterior, en versiones anteriores de Visual C++ y debido a los cambios relacionados con el análisis de cadenas, los literales de cadena adyacentes (ya fueran literales de cadena de caracteres anchos o estrechos) sin ningún espacio en blanco se interpretaban como una sola cadena concatenada. En Visual Studio 2015, ahora debe agregarse un espacio en blanco entre las dos cadenas. Por ejemplo, el código siguiente debe modificarse:
char * str = "abc""def";
Para corregir este error, agregue un espacio entre las dos cadenas:
char * str = "abc" "def";
Placement new y delete
Se ha realizado un cambio en el operador delete
a fin de adaptarlo al estándar de C++14. Detalles del cambio de los estándares se pueden encontrar en la página de desasignación de ajuste de tamaño de C++. Los cambios agregan un formulario del operador delete
global que toma un parámetro de tamaño. La novedad es que si antes se usaba un operador delete
con la misma signatura (para que se correspondiese con un operador placement new), ahora recibirá un error del compilador (C2956, que se produce en el punto donde se usa placement new, ya que es la posición en el código en la que el compilador intenta identificar un operador delete
coincidente adecuado).
La función void operator delete(void *, size_t)
era un operador placement delete correspondiente a la función placement newvoid * operator new(size_t, size_t)
en C++11. Con la desasignación con tamaño de C ++ 14, ahora esta función delete es una función de desasignación habitual (operador delete
global). Según el estándar, si el uso de placement new busca una función delete correspondiente y encuentra una función de desasignación habitual, el programa tendrá un formato incorrecto.
Supongamos, por ejemplo, que el código define tanto placement new como placement delete:
void * operator new(std::size_t, std::size_t);
void operator delete(void*, std::size_t) noexcept;
El problema se produce debido a la coincidencia de las signaturas de función entre un operador placement delete definido y el nuevo operador global delete
con tamaño. Analice si puede usar un tipo que no sea size_t
para los operadores placement new y delete
. El tipo del size_t
typedef
depende del compilador; es un typedef
para unsigned int
en MSVC. Una buena solución consiste en usar un tipo enumerado como este:
enum class my_type : size_t {};
A continuación, cambie la definición de placement new y delete
para usar este tipo como el segundo argumento en lugar de size_t
. También es necesario que actualice las llamadas a placement new para pasar el nuevo tipo (por ejemplo, con static_cast<my_type>
para convertir a partir del valor entero) y que actualice la definición de new
y delete
para realizar la conversión al tipo entero. No es necesario usar enum
para esto; un tipo de clase con un miembro size_t
también funcionaría.
Una solución alternativa es que pueda eliminar por completo placement new. Si el código usa placement new para implementar un bloque de memoria donde el argumento placement sea el tamaño del objeto que se va a asignar o eliminar, entonces podría usarse la característica de desasignación con tamaño para reemplazar su propio código de bloque de memoria personalizado, y así deshacerse de las funciones placement y usar simplemente su propio operador delete
de dos argumentos en lugar de las funciones placement.
Si no quiere actualizar su código de forma inmediata, puede recuperar el comportamiento anterior mediante la opción de compilador /Zc:sizedDealloc-
. Si usa esta opción, las funciones delete de dos argumentos no existen y no se provocará un conflicto con el operador placement delete.
Miembros de datos de uniones
Los miembros de datos de uniones ya no pueden tener tipos de referencia. El siguiente código se compila en Visual Studio 2013, pero genera un error en Visual Studio 2015.
union U1
{
const int i;
};
union U2
{
int & i;
};
union U3
{
struct { int & i; };
};
El código anterior produce los errores siguientes:
test.cpp(67): error C2625: 'U2::i': illegal union member; type 'int &' is reference type
test.cpp(70): error C2625: 'U3::i': illegal union member; type 'int &' is reference type
Para solucionar este problema, cambie los tipos de referencia a un puntero o a un valor. Para poder cambiar el tipo a un puntero, hay que hacer cambios en el código que usa el campo union. Si se cambia el código a un valor, también cambian los datos almacenados en la unión, lo que afecta a otros campos, ya que los campos de tipos union comparten la misma memoria. En función del tamaño del valor, también podrá cambiar el tamaño de la unión.
Las uniones anónimas ahora se ajustan mejor al estándar. Las versiones anteriores del compilador generaban un constructor y destructor explícitos para uniones anónimas. En Visual Studio 2015 se eliminan estas funciones generadas por el compilador.
struct S
{
S();
};
union
{
struct
{
S s;
};
} u;
El código anterior genera el error siguiente en Visual Studio 2015:
error C2280: '<unnamed-type-u>::<unnamed-type-u>(void)': attempting to reference a deleted function
note: compiler has generated '<unnamed-type-u>::<unnamed-type-u>' here
Para resolver este problema, proporcione sus propias definiciones del constructor o del destructor.
struct S
{
S() {}
};
union
{
struct
{
S s;
};
} u;
Uniones con estructuras anónimas
Para cumplir con el estándar, el comportamiento de runtime ha cambiado para los miembros de estructuras anónimas en uniones. El constructor de miembros de estructura anónima de una unión ya no se llama implícitamente cuando se crea este tipo de unión. Además, el destructor de miembros de estructura anónima de una unión ya no se llama implícitamente cuando la unión sale del ámbito. Analice el código siguiente, en el que una unión U contiene una estructura anónima que contiene un miembro que es una estructura con nombre S que tiene un destructor.
#include <stdio.h>
struct S
{
S() { printf("Creating S\n"); }
~S() { printf("Destroying S\n"); }
};
union U
{
struct {
S s;
};
U() {}
~U() {}
};
void f()
{
U u;
}
int main()
{
f();
char s[1024];
printf("Press any key.\n");
gets_s(s);
return 0;
}
En Visual Studio 2013, se llama al constructor de S cuando se crea la unión, y se llama al destructor de S cuando se limpia la pila de la función f. Pero en Visual Studio 2015, no se llama al constructor ni al destructor. El compilador emite una advertencia sobre este cambio de comportamiento.
warning C4587: 'U::s': behavior change: constructor is no longer implicitly calledwarning C4588: 'U::s': behavior change: destructor is no longer implicitly called
Para restaurar el comportamiento original, asigne un nombre a la estructura anónima. El comportamiento en tiempo de ejecución de las estructuras no anónima es el mismo, independientemente de la versión del compilador.
#include <stdio.h>
struct S
{
S() { printf("Creating S.\n"); }
~S() { printf("Destroying S\n"); }
};
union U
{
struct
{
S s;
} namedStruct;
U() {}
~U() {}
};
void f()
{
U u;
}
int main()
{
f();
char s[1024];
printf("Press any key.\n");
gets_s(s);
return 0;
}
Por otro lado, intente mover el código del constructor y el destructor a funciones nuevas y agregue llamadas a estas funciones desde el constructor y el destructor de la unión.
#include <stdio.h>
struct S
{
void Create() { printf("Creating S.\n"); }
void Destroy() { printf("Destroying S\n"); }
};
union U
{
struct
{
S s;
};
U() { s.Create(); }
~U() { s.Destroy(); }
};
void f()
{
U u;
}
int main()
{
f();
char s[1024];
printf("Press any key.\n");
gets_s(s);
return 0;
}
Resolución de plantilla
Se han hecho cambios en la resolución de nombres de plantillas. En C++, al pensar en candidatos para la resolución de un nombre, puede darse el caso de que uno o más de los nombres considerados como coincidencias posibles produzcan una creación de instancias de plantilla no válida. Normalmente, estas instancias no válidas no provocan errores del compilador, un principio conocido como SFINAE (Substitution Failure Is Not An Error).
Ahora, si SFINAE requiere que el compilador cree una instancia de la especialización de una plantilla de clase, los errores que se producen durante este proceso son errores del compilador. En versiones anteriores, el compilador omitiría esos errores. Por ejemplo, suponga el siguiente código:
#include <type_traits>
template< typename T>
struct S
{
S() = default;
S(const S&);
S(S& &);
template< typename U, typename = typename std::enable_if< std::is_base_of< T, U> ::value> ::type>
S(S< U> & &);
};
struct D;
void f1()
{
S< D> s1;
S< D> s2(s1);
}
struct B
{
};
struct D : public B
{
};
void f2()
{
S< D> s1;
S< D> s2(s1);
}
Si usa el compilador actual, obtendrá el siguiente error:
type_traits(1110): error C2139: 'D': an undefined class is not allowed as an argument to compiler intrinsic type trait '__is_base_of'
..\t331.cpp(14): note: see declaration of 'D'
..\t331.cpp(10): note: see reference to class template instantiation 'std::is_base_of<T,U>' being compiled
with
[
T=D,
U=D
]
Esto se debe a que el punto de la primera invocación de is_base_of de la clase D
todavía no se ha definido.
En este caso, la solución consiste en no usar rasgos de tipos hasta que no se haya definido la clase. Si mueve las definiciones de B
y D
al principio del archivo de código, el error se soluciona. Si las definiciones están en archivos de encabezado, compruebe el orden de las instrucciones include para los archivos de encabezado a fin de asegurarse de que las definiciones de clase se compilan antes de que se usen las plantillas problemáticas.
Constructores de copias
Tanto en Visual Studio 2013 como en Visual Studio 2015, el compilador genera un constructor de copias para una clase si esa clase tiene un constructor de movimiento definido por el usuario, pero ningún constructor de copias definido por el usuario. En Dev14, este constructor de copias generado implícitamente también se marca como "= delete".
main declarado como "C" externo requiere un tipo de valor devuelto.
El código siguiente genera ahora C4430.
extern "C" __cdecl main(){}
Para corregir el error, agregue el tipo de valor devuelto:
extern "C" int __cdecl main(){}
typename no se permite en un inicializador de miembro
El código siguiente genera ahora C2059.
template<typename T>
struct S1 : public T::type
{
S1() : typename T::type()
{
}
};
struct S2 {
typedef S2 type;
};
S1<S2> s;
Para corregir el error, quite typename
del inicializador:
S1() : T::type()
...
La clase de almacenamiento en especializaciones explícitas se omite.
En el siguiente código, se omite el especificador de clase de almacenamiento estático.
template <typename T>
void myfunc(T h)
{
}
template<>
static void myfunc(double h)
{
}
Una constante usada en un elemento static_assert dentro de un aplantilla de clase siempre dará error.
El código siguiente provoca que static_assert
siempre genere error:
template <size_t some_value>
struct S1
{
static_assert(false, "default not valid");
};
Para solucionar este problema, encapsule el valor en un elemento struct
:
template <size_t some_value>
struct constant_false {
static const bool value = false;
};
template <size_t some_value>
struct S1
{
static_assert(constant_false<some_value>::value, "default not valid");
};
Reglas aplicadas para declaraciones adelantadas. (Solo se aplica a C.)
El código siguiente produce ahora C2065:
struct token_s;
typedef int BOOL;
typedef int INT;
typedef int(*PFNTERM)(PTOKEN, BOOL, INT);
Para corregir este problema, agregue las declaraciones adelantadas adecuadas:
struct token_s;
typedef int BOOL;
typedef int INT;
typedef struct token_s TOKEN;
typedef TOKEN *PTOKEN;
typedef int(*PFNTERM)(PTOKEN, BOOL, INT);
Cumplimiento más coherente de tipos de puntero de función
El código siguiente produce ahora C2197:
typedef int(*F1)(int);
typedef int(*F2)(int, int);
void func(F1 f, int v1, int v2)
{
f(v1, v2);
}
Llamadas ambiguas a funciones sobrecarfadas
El código siguiente produce ahora C266: 'N::bind': llamada ambigua a una función sobrecargada
template<typename R, typename T, typename T1, typename A1>
void bind(R(T::*)(T1), A1&&);
namespace N
{
template <typename T, typename R, typename ... Tx>
void bind(R(T::*)(Tx...), T* ptr);
}
using namespace N;
class Manager
{
public:
void func(bool initializing);
void mf()
{
bind(&Manager::func, this);
}
};
Para corregir el error, puede calificar completamente la llamada a bind: N::bind(...)
. Sin embargo, si este cambio se manifiesta mediante un identificador sin declarar (C2065), puede ser adecuado corregirlo con una declaración using
en su lugar.
Este patrón se produce con frecuencia con ComPtr y otros tipos en el espacio de nombres Microsoft::WRL
.
Corrija la dirección incorrecta de
El código siguiente produce ahora C2440: "=": no se puede convertir de "type *" a "type". Para corregir el error, cambie &(type) por (type) y (&f()) por (f()).
typedef void (*type)(void);
void f(int i, type p);
void g(int);
void h(void)
{
f(0, &(type)g);
}
typedef void(*type)(void);
type f();
void g(type);
void h()
{
g(&f());
}
El literal de cadena es una matriz constante
El código siguiente produce ahora C2664: "void f(void )": el argumento 1 no se puede convertir de "const char ()[2]" a "void *".
void f(void *);
void h(void)
{
f(&__FUNCTION__);
void *p = &"";
}
Para corregir el error, cambie el tipo de parámetro de función a const void*
, o bien cambie el cuerpo de h
para que sea similar a este ejemplo:
void h(void)
{
char name[] = __FUNCTION__;
f( name);
void *p = &"";
}
Cadenas UDL C++11
El código siguiente produce ahora el error C3688: sufijo literal no válido "L"; no se encontró el operador literal o la plantilla de operador literal ""L'.
#define MACRO
#define STRCAT(x, y) x\#\#y
int main(){
auto *val1 = L"string"MACRO;
auto *val2 = L"hello "L"world";
std::cout << STRCAT(L"hi ", L"there");
}
Para corregir el error, cambie el código para agregar un espacio:
#define MACRO
#define STRCAT(x, y) x y
int main(){
auto *val1 = L"string" MACRO;
auto *val2 = L"hello " L"world";
std::cout << STRCAT(L"hi ", L"there");
}
En el ejemplo anterior, MACRO
ya no se analiza como dos tokens (una cadena seguida de una macro). Ahora se analiza como un UDL de token único. Lo mismo se aplica a L""L"", que se analizó anteriormente como L"" y L"", y que ahora se analiza como L""L y "".
Las reglas de concatenación de cadenas también se formularon conformes al estándar, lo que significa que L"a" "b" es equivalente a L"ab". En las ediciones anteriores de Visual Studio no se aceptaba la concatenación de cadenas con un ancho de carácter diferente.
Carácter vacío quitado de C++11
El código siguiente produce ahora el error C2137: constante de carácter vacío
bool check(wchar_t c){
return c == L''; //implicit null character
}
Para corregir el error, cambie el código para hacer que NULL sea explícito:
bool check(wchar_t c){
return c == L'\0';
}
El valor no puede capturar excepciones de MFC porque no se pueden copiar
El siguiente código en una aplicación de MFC produce ahora el error C2316: 'D': no se puede capturar porque el destructor o el constructor de copia son inaccesibles o se han eliminado.
struct B {
public:
B();
private:
B(const B &);
};
struct D : public B {
};
int main()
{
try
{
}
catch (D)
{
}
}
Para corregir el código, puede cambiar el bloque catch a catch (const D &)
pero la mejor solución suele ser usar las macros TRY/CATCH de MFC.
alignof
ahora es una palabra clave.
El siguiente código produce ahora el error C2332: "clase": falta nombre de etiqueta. Para corregir el código, debe cambiar el nombre de la clase o, si la clase realiza el mismo trabajo que alignof
, simplemente sustituir la clase por la palabra clave new.
class alignof{}
constexpr
ahora es una palabra clave.
El código siguiente produce ahora el error C2059: error de sintaxis: ')'. Para corregir el código, debe cambiar el nombre de las funciones o las variables que se llamen constexpr
.
int constexpr() {return 1;}
Los tipos que se pueden mover no pueden ser const
Cuando una función devuelve un tipo destinado a moverse, su tipo de valor devuelto no debe ser const
.
Constructores de copia eliminados
El código siguiente genera ahora el error C2280 'S::S(S &&)': se está intentando hacer referencia a una función eliminada:
struct S{
S(int, int);
S(const S&) = delete;
S(S&&) = delete;
};
S s2 = S(2, 3);
Para corregir el error, use la inicialización directa para S2
:
struct S{
S(int, int);
S(const S&) = delete;
S(S&&) = delete;
};
S s2 = {2,3};
Conversión a puntero de función solo generada cuando no hay captura lambda
El código siguiente produce el error C2664 en Visual Studio 2015.
void func(int(*)(int)) {}
int main() {
func([=](int val) { return val; });
}
Para corregir el error, quite =
de la lista de captura.
Llamadas ambiguas que afectan a operadores de conversión
El código siguiente produce ahora el error C2440: "conversión de tipo": no se puede convertir de "S2" a "S1":
struct S1 {
S1(int);
};
struct S2 {
operator S1();
operator int();
};
void f(S2 s2)
{
(S1)s2;
}
Para corregir el error, debe llamar explícitamente al operador de conversión:
void f(S2 s2)
{
s2.operator S1();
S1((int)s2);
}
El código siguiente produce ahora el error C2593: "operador =" es ambiguo:
struct S1 {};
struct S2 {
operator S1&();
operator S1() const;
};
void f(S1 *p, S2 s)
{
*p = s;
}
Para corregir el error, debe llamar explícitamente al operador de conversión:
void f(S1 *p, S2 s)
{
*p = s.operator S1&();
}
Corregir inicialización de copia no válida en inicialización de miembro no estático (NSDMI)
El código siguiente genera ahora el error C2664: "S1::S1(S1 &&)": no se puede convertir el argumento 1 de "bool" a "const S1 &":
struct S1 {
explicit S1(bool);
};
struct S2 {
S1 s2 = true;
};
Para corregir el error, use la inicialización directa:
struct S2 {
S1 s1{true};
};
Acceso a constructores dentro de instrucciones decltype
El código siguiente genera ahora el error C2248: "S::S": no se puede acceder al miembro privado declarado en la clase "S":
class S {
S();
public:
int i;
};
class S2 {
auto f() -> decltype(S().i);
};
Para corregir el error, agregue una declaración friend para S2
en S
:
class S {
S();
friend class S2;
public:
int i;
};
El constructor predeterminado de lambda se ha eliminado implícitamente
El código siguiente produce ahora el error C3497: no puede construir una instancia de una expresión lambda:
void func(){
auto lambda = [](){};
decltype(lambda) other;
}
Para corregir el error, elimine la necesidad de llamar al constructor predeterminado. Si la expresión lambda no captura nada, se puede convertir en un puntero de función.
Expresiones lambda con un operador de asignación eliminado
El código siguiente produce ahora el error C2280:
#include <memory>
#include <type_traits>
template <typename T, typename D>
std::unique_ptr<T, typename std::remove_reference<D &&>::type> wrap_unique(T *p, D &&d);
void f(int i)
{
auto encodedMsg = wrap_unique<unsigned char>(nullptr, [i](unsigned char *p) {
});
encodedMsg = std::move(encodedMsg);
}
Para corregir el error, reemplace la expresión lambda por una clase functor o elimine la necesidad de usar el operador de asignación.
Se está intentando mover un objeto con constructor de copia eliminado
El código siguiente produce ahora el error C2280: "'moveable::moveable(const moveable &)": se está intentando hacer referencia a una función eliminada
struct moveable {
moveable() = default;
moveable(moveable&&) = default;
moveable(const moveable&) = delete;
};
struct S {
S(moveable && m) :
m_m(m)
{}
moveable m_m;
};
Para resolver el error, use std::move
en su lugar:
S(moveable && m) :
m_m(std::move(m))
La clase local no puede hacer referencia a otra clase local definida posteriormente en la misma función
El código siguiente produce ahora el error C2079: "s" usa struct "main::S2" sin definir.
int main()
{
struct S2;
struct S1 {
void f() {
S2 s;
}
};
struct S2 {};
}
Para corregir el error, mueva arriba la definición de S2
:
int main()
{
struct S2 {
};
struct S1 {
void f() {
S2 s;
}
};
}
No se puede llamar a un ctor base protegido en el cuerpo de ctor derivado.
El código siguiente produce ahora el error C2248: "S1::S1": no se puede acceder al miembro protegido declarado en la clase "S1"
struct S1 {
protected:
S1();
};
struct S2 : public S1 {
S2() {
S1();
}
};
Para corregir el error, en S2
, quite la llamada a S1()
desde el constructor y, si es necesario, colóquela en otra función.
{} impide la conversión a puntero
El código siguiente produce ahora el error C2439 "S::p": no se pudo inicializar el miembro
struct S {
S() : p({ 0 }) {}
void *p;
};
Para corregir el error, quite las llaves de alrededor de 0
, o bien use en su lugar nullptr
, como se muestra en este ejemplo:
struct S {
S() : p(nullptr) {}
void *p;
};
Definición incorrecta de macro y uso con paréntesis
El código siguiente produce ahora el error C2008: ";": no se esperaba en la definición de macro
#define A;
struct S {
A();
};
Para corregir el problema, cambie la línea superior a #define A();
.
El código siguiente produce el error C2059: error de sintaxis: ")"
#define A () ;
struct S {
A();
};
Para corregir el código, quite el espacio entre A y ().
El código siguiente genera el error C2091: la función devuelve una función:
#define DECLARE void f()
struct S {
DECLARE();
};
Para corregir el error, quite los paréntesis después DECLARE en S: DECLARE;
.
El código siguiente produce el error C2062: tipo "int" inesperado
#define A (int)
struct S {
A a;
};
Para corregir el problema, defina A
de la siguiente manera:
#define A int
Paréntesis adicionales en las declaraciones
El código siguiente produce el error C2062: tipo "int" inesperado
struct S {
int i;
(int)j;
};
Para corregir el error, quite los paréntesis de j
. Si los paréntesis son necesarios por motivos de claridad, use typedef
.
Constructores generados por el compilador y __declspec(novtable)
En Visual Studio 2015, existe una mayor probabilidad de que los constructores insertados de clases abstractas generados por el compilador con clases base virtuales puedan exponer el uso inadecuado de __declspec(novtable)
cuando se usa en combinación con __declspec(dllimport)
.
auto requiere una sola expresión en direct-list-initialization
El código siguiente produce ahora el error C3518: "testPositions": en un contexto de inicialización de lista directa, el tipo para "auto" solo se puede deducir a partir de una sola expresión de inicializador
auto testPositions{
std::tuple<int, int>{13, 33},
std::tuple<int, int>{-23, -48},
std::tuple<int, int>{38, -12},
std::tuple<int, int>{-21, 17}
};
Para corregir el error, una posibilidad es inicializar testPositions
de la manera siguiente:
std::tuple<int, int> testPositions[]{
std::tuple<int, int>{13, 33},
std::tuple<int, int>{-23, -48},
std::tuple<int, int>{38, -12},
std::tuple<int, int>{-21, 17}
};
Comprobación de tipos frente a punteros a tipos de is_convertible
El código siguiente provoca ahora que la aserción estática produzca error.
struct B1 {
private:
B1(const B1 &);
};
struct B2 : public B1 {};
struct D : public B2 {};
static_assert(std::is_convertible<D, B2>::value, "fail");
Para corregir el error cambie static_assert
para que compare los punteros a D
y B2
:
static_assert(std::is_convertible<D*, B2*>::value, "fail");
las declaraciones __declspec(novtable) deben ser coherentes
Las declaraciones de __declspec
deben ser coherentes en todas las bibliotecas. El siguiente código producirá ahora una infracción de regla ahora producirá una infracción de una regla de definición (ODR):
class __declspec(dllexport)
A {
public:
A();
A(const A&);
virtual ~A();
private:
int i;
};
A::A() {}
A::~A() {}
A::A(const A&) {}
#pragma comment(lib, "A")
class __declspec(dllimport) A
{
public: A();
A(const A&);
virtual ~A();
private:
int i;
};
struct __declspec(novtable) __declspec(dllexport) B
: virtual public A {
virtual void f() = 0;
};
#pragma comment(lib, "A")
#pragma comment(lib, "B")
class __declspec(dllimport) A
{
public:
A();
A(const A&);
virtual ~A();
private:
int i;
};
struct /* __declspec(novtable) */ __declspec(dllimport) B // Error. B needs to be novtable here also.
: virtual public A
{
virtual void f() = 0;
};
struct C : virtual B
{
virtual void f();
};
void C::f() {}
C c;
Clases base virtuales privadas y herencia indirecta
En las versiones anteriores del compilador se permitía que una clase derivada llamase a funciones miembro de sus clases base private virtual
derivadas de forma indirecta. Este comportamiento anterior era incorrecto y no se ajusta al estándar de C++. El compilador ya no acepta código escrito de este modo y emite el error del compilador C2280 como resultado.
error C2280: 'void *S3::__delDtor(unsigned int)': attempting to reference a deleted function
Ejemplo (antes)
class base
{
protected:
base();
~base();
};
class middle : private virtual base {}; class top : public virtual middle {};
void destroy(top *p)
{
delete p;
}
Ejemplo (después)
class base;
class middle : protected virtual base {};
class top : public virtual middle {};
void destroy(top *p)
{
delete p;
}
O bien
class base;
class middle : private virtual base {};
class top : public virtual middle, private virtual bottom {};
void destroy(top *p)
{
delete p;
}
Operador new y operador delete sobrecargados
Las versiones anteriores del compilador permitieron que operator new no miembro y operator delete no miembro se declarasen como static y que se declararan en espacios de nombres distintos del espacio de nombres global. Este comportamiento anterior crea un riesgo de que el programa no llame a la implementación del operador new
o delete
que el programador diseñó, generando un comportamiento en tiempo de ejecución incorrecto silencioso. El compilador ya no acepta el código escrito de este modo y emite el error del compilador C2323 en su lugar.
error C2323: 'operator new': non-member operator new or delete functions may not be declared static or in a namespace other than the global namespace.
Ejemplo (antes)
static inline void * __cdecl operator new(size_t cb, const std::nothrow_t&)
Ejemplo (después)
void * __cdecl operator new(size_t cb, const std::nothrow_t&)
Además, aunque el compilador no ofrece un diagnóstico específico, se considera que el operador en línea new
está mal formado.
Llamada a "operator type()" (conversión definida por el usuario) en tipos que no son de clase
Las versiones anteriores del compilador permitieron que se llamara 'operator type()' en tipos que no son de clase mientras que se les ignora en modo silencioso. Este comportamiento anterior creó un riesgo de generación de código incorrecto silencioso, lo que produjo un comportamiento impredecible en tiempo de ejecución. El compilador ya no acepta el código escrito de este modo y emite el error del compilador C2228 en su lugar.
error C2228: left of '.operator type' must have class/struct/union
Ejemplo (antes)
typedef int index_t;
void bounds_check(index_t index);
void login(int column)
{
bounds_check(column.operator index_t());
}
Ejemplo (después)
typedef int index_t;
void bounds_check(index_t index);
void login(int column)
{
bounds_check(column);
}
Typename redundante en los especificadores de tipos elaborados
Las versiones anteriores del compilador permitieron typename
en especificadores de tipo elaborados, pero el código escrito de este modo es incorrecto semánticamente. El compilador ya no acepta el código escrito de este modo y emite el error del compilador C3406 en su lugar.
error C3406: 'typename' cannot be used in an elaborated type specifier
Ejemplo (antes)
template <typename class T>
class container;
Ejemplo (después)
template <class T> // alternatively, could be 'template <typename T>'; 'typename' is not elaborating a type specifier in this case
class container;
Deducción de tipos de matrices de una lista de inicializadores
Las versiones anteriores del compilador no admitían la deducción de tipos de matrices de una lista de inicializadores. El compilador admite ahora esta forma de deducción de tipos y, como resultado, las llamadas a las plantillas de función mediante listas de inicializadores podrían ser ahora ambiguas o se podría elegir una sobrecarga diferente de la de versiones anteriores del compilador. Para resolver estos problemas, el programa debe especificar ahora explícitamente la sobrecarga que el programador tiene como objetivo.
Cuando este comportamiento nuevo hace que la resolución de sobrecarga considere que un candidato adicional es igual de bueno que el candidato histórico, la llamada pasa a ser ambigua y el compilador emite el error del compilador C2668 como resultado.
error C2668: 'function' : ambiguous call to overloaded function.
Ejemplo 1: llamada ambigua a una función sobrecargada (antes)
template < typename... Args>
void f(int, Args...);
template < int N, typename... Args>
void f(const int(&)[N], Args...);
int main()
{
f({ 3 }); error C2668 : 'f' ambiguous call to overloaded function
}
Ejemplo 1: llamada ambigua a una función sobrecargada (después)
template < typename... Args>
void f(int, Args...);
template < int N, typename... Args>
void f(const int(&)[N], Args...);
int main()
{
f(3);
}
Cuando este comportamiento nuevo provoca que la resolución de sobrecarga considere que un candidato adicional es una coincidencia mejor que el candidato histórico, la llamada se resuelve sin ambigüedades en el nuevo candidato, lo que provoca un cambio en el comportamiento del programa que probablemente es distinto de lo que el programador pretendía.
Ejemplo 2: cambio en la resolución de sobrecarga (antes)
struct S
{
int i;
int j;
};
template < typename... Args>
void f(S, Args...);
template < int N, typename... Args>
void f(const int *&)[N], Args...);
int main()
{
f({ 1, 2 });
}
Ejemplo 2: cambio en la resolución de sobrecarga (después)
struct S;
template < typename... Args>
void f(S, Args...);
template < int N, typename... Args>
void f(const int *&)[N], Args...);
int main()
{
f(S{ 1, 2 });
}
Restauración de las advertencias de la instrucción switch
En una versión anterior del compilador se quitaron algunas advertencias relacionadas con las instrucciones switch
; ahora, estas advertencias se han restaurado. El compilador emite ahora las advertencias restauradas y se emiten ahora advertencias relacionadas con los casos específicos (incluido el caso predeterminado) en la línea que contiene el caso que genera el error, en lugar de en la última línea de la instrucción switch. Como resultado de emitir ahora esas advertencias en líneas diferentes a como se hacía en el pasado, ya no se podrán suprimir según lo previsto las advertencias suprimidas anteriormente mediante #pragma warning(disable:####)
. Para suprimir estas advertencias según lo previsto, es posible que sea necesario mover la directiva #pragma warning(disable:####)
a una línea por encima del primer caso en el que se genera el error. Las siguientes son las advertencias restauradas:
warning C4060: switch statement contains no 'case' or 'default' labels
warning C4061: enumerator 'bit1' in switch of enum 'flags' is not explicitly handled by a case label
warning C4062: enumerator 'bit1' in switch of enum 'flags' is not handled
warning C4063: case 'bit32' is not a valid value for switch of enum 'flags'
warning C4064: switch of incomplete enum 'flags'
warning C4065: switch statement contains 'default' but no 'case' labels
warning C4808: case 'value' is not a valid value for switch condition of type 'bool'
Warning C4809: switch statement has redundant 'default' label; all possible 'case' labels are given
Ejemplo de C4063: (antes)
class settings
{
public:
enum flags
{
bit0 = 0x1,
bit1 = 0x2,
...
};
...
};
int main()
{
auto val = settings::bit1;
switch (val)
{
case settings::bit0:
break;
case settings::bit1:
break;
case settings::bit0 | settings::bit1:
break;
}
};
Ejemplo de C4063: (después)
class settings { ... };
int main()
{
typedef std::underlying_type< settings::flags> ::type flags_t;
auto val = settings::bit1;
switch (static_cast< flags_t> (val))
{
case settings::bit0:
break;
case settings::bit1:
break;
case settings::bit0 | settings::bit1:
break;
}
};
En su documentación se ofrecen ejemplos de las otras advertencias restauradas.
#include: uso del especificador de principal-directorio '..' en nombre de ruta (solo afecta a /Wall
/WX
)
Las versiones anteriores del compilador no detectaron el uso del especificador de principal-directorio '..' en el nombre de la ruta de acceso de las directivas #include
. El código escrito de este modo normalmente está pensado para incluir encabezados que existen fuera del proyecto usando incorrectamente rutas de acceso relativas del proyecto. Este comportamiento antiguo crea un riesgo de que el programa se pueda compilar incluyendo un archivo de origen diferente del que pensaba el programador, o que esas rutas de acceso relativas no son portables a otros entornos de compilación. Ahora el compilador detecta y notifica al programador del código escrito de esta manera y emite una advertencia C4464 de compilador opcional, si se habilita.
warning C4464: relative include path contains '..'
Ejemplo (antes)
#include "..\headers\C4426.h"
Ejemplo (después)
#include "C4426.h"
Además, aunque el compilador no ofrece un diagnóstico específico, se recomienda no usar el especificador de directorio principal ".." para especificar los directorios de inclusión del proyecto.
#pragma optimize() amplía el final del archivo de encabezado (solo afecta a /Wall
/WX
)
Las versiones anteriores del compilador no detectaban cambios en la configuración de la marca de optimización que eluden un archivo de encabezado incluido dentro de una unidad de traducción. Ahora el compilador detecta y notifica al programador del código escrito de esta manera y emite una advertencia C4426 de compilador opcional en la ubicación de #include
que genera el problema, si se habilita. Esta advertencia solo se emite si el cambio entra en conflicto con las marcas de optimización establecidas por los argumentos de la línea de comandos para el compilador.
warning C4426: optimization flags changed after including header, may be due to #pragma optimize()
Ejemplo (antes)
#pragma optimize("g", off)
...
#include "C4426.h"
Ejemplo (después)
#pragma optimize("g", off)
...
#pragma optimize("", on)
#include "C4426.h"
Error #pragma advertencia(push) y #pragma advertencia(pop) (solo afecta a /Wall
/WX
)
Las versiones anteriores del compilador no detectaban que los cambios de estado #pragma warning(push)
se emparejaban con cambios de estado #pragma warning(pop)
en un archivo de código fuente diferente, lo que rara vez es lo previsto. Este comportamiento antiguo creaba un riesgo de que el programa se compilara con un conjunto de advertencias habilitadas diferentes de lo que el programador había pensado, lo cual puede provocar un comportamiento en tiempo de ejecución incorrecto silencioso. Ahora el compilador detecta el código escrito de esta manera, lo notifica al programador y emite una advertencia del compilador opcional C5031 en la ubicación de #pragma warning(pop)
coincidente, si se habilita. Esta advertencia incluye una nota que hace referencia a la ubicación de #pragma warning(push) correspondiente.
warning C5031: #pragma warning(pop): likely mismatch, popping warning state pushed in different file
Ejemplo (antes)
#pragma warning(push)
#pragma warning(disable:####)
...
...
#pragma warning(pop)
...
#include "C5031_part1.h"
...
#include "C5031_part2.h"
...
Ejemplo (después)
#pragma warning(push)
#pragma warning(disable:####)
...
#pragma warning(pop)
#pragma warning(push)
#pragma warning(disable:####)
...
#pragma warning(pop)
#include "C5031_part1.h"
...
#include "C5031_part2.h"
...
Aunque es poco frecuente, a veces el código escrito de este modo es intencionado. El código escrito de este modo es sensible a los cambios en el orden de #include
. Cuando sea posible, se recomienda que los archivos de código fuente administren el estado de advertencia de forma independiente.
Sin coincidencia #pragma advertencia (push) (solo afecta a /Wall
/WX
)
Las versiones anteriores del compilador no detectaron cambios de estado #pragma warning(push)
sin coincidencia al final de una unidad de traducción. Ahora el compilador detecta el código escrito de esta manera, lo notifica al programador y emite una advertencia del compilador opcional C5032 en la ubicación de #pragma warning(push)
no coincidente, si se habilita. Esta advertencia solo se emite si no hay ningún error de compilación en la unidad de traducción.
warning C5032: detected #pragma warning(push) with no corresponding #pragma warning(pop)
Ejemplo (antes)
#pragma warning(push)
#pragma warning(disable:####)
...
#include "C5032.h"
...
Ejemplo (después)
#pragma warning(push)
#pragma warning(disable:####)
...
#pragma warning(pop)
#include "C5032.h"
...
Se pueden emitir advertencias adicionales como resultado del seguimiento del estado de advertencia #pragma mejorado
Las versiones anteriores del compilador no realizaron un seguimiento de los cambios de estados de advertencia #pragma lo suficientemente bueno como para emitir todas las advertencias que se pretendían. Este comportamiento provocó un riesgo de que se suprimirían algunas advertencias de manera eficaz en circunstancias diferentes de las que tenía previstas el programador. Ahora, el compilador realiza el seguimiento del estado #pragma warning
con mayor eficacia, especialmente en relación con los cambios de estado #pragma warning
dentro de las plantillas y, opcionalmente, emite nuevas advertencias C5031 y C5032, que están pensadas para ayudar al programador a localizar usos imprevistos de #pragma warning(push)
y #pragma warning(pop)
.
Como resultado del seguimiento del cambio de estado de #pragma warning
mejorado, es posible emitir ahora las advertencias anteriormente suprimidas de manera incorrecta o las advertencias relacionadas con problemas que anteriormente tenían un mal diagnóstico.
Mejora de la identificación del código inaccesible
Los cambios en la biblioteca estándar de C++ y la mejora en la capacidad para llamadas a funciones insertadas respecto a versiones anteriores del compilador pueden permitirle a este demostrar que determinado código ahora es inaccesible. Este nuevo comportamiento puede producir nuevas instancias de advertencia C4720 emitidas con mayor frecuencia.
warning C4720: unreachable code
En muchos casos, esta advertencia solo se puede emitir al compilar con las optimizaciones habilitadas, puesto que las optimizaciones pueden insertar más llamadas a funciones, eliminar código redundante o hacer posible de otra manera que se determine cierto código que no sea accesible. Hemos observado que las instancias nuevas de la advertencia C4720 han aparecido con frecuencia en bloques try/catch, especialmente en relación con el uso de std::find.
Ejemplo (antes)
try
{
auto iter = std::find(v.begin(), v.end(), 5);
}
catch (...)
{
do_something();
}
Ejemplo (después)
try
{
auto iter = std::find(v.begin(), v.end(), 5);
}
catch (...)
{
do_something();
}
Puede que se generen errores y advertencias adicionales como resultado de la compatibilidad parcial con la expresión SFINAE
Las versiones anteriores del compilador no analizaban determinados tipos de expresiones dentro de especificadores decltype
debido a la falta de compatibilidad con la expresión SFINAE. Este comportamiento anterior era incorrecto y no se ajusta al estándar de C++. Ahora, el compilador analiza estas expresiones y tiene compatibilidad parcial para la expresión SFINAE gracias a las mejoras de cumplimiento continuas. Como resultado, ahora, el compilador genera advertencias y errores detectados en las expresiones que las versiones anteriores del compilador no analizaban.
Cuando este comportamiento nuevo analiza una expresión decltype
en la que se incluye un tipo que todavía no se ha declarado, el compilador genera el error C2039 como resultado.
error C2039: 'type': is not a member of 'global namespace'
Ejemplo 1: uso de un tipo no declarado (antes)
struct s1
{
template < typename T>
auto f() - > decltype(s2< T> ::type::f());
template< typename>
struct s2 {};
}
Ejemplo 1 (después)
struct s1
{
template < typename>
template < typename T>
auto f() - > decltype(s2< T> ::type::f());
template< typename>
struct s2 {};
}
Cuando este comportamiento nuevo analiza una expresión decltype
en la que falta el uso necesario de la palabra clave typename
para especificar que un nombre dependiente es un tipo, el compilador emite la advertencia del compilador C4346, junto con el error del compilador C2923.
warning C4346: 'S2<T>::Type': dependent name is not a type
error C2923: 's1': 'S2<T>::Type' is not a valid template type argument for parameter 'T'
Ejemplo 2: un nombre dependiente no es un tipo (antes)
template < typename T>
struct s1
{
typedef T type;
};
template < typename T>
struct s2
{
typedef T type;
};
template < typename T>
T declval();
struct s
{
template < typename T>
auto f(T t) - > decltype(t(declval< S1< S2< T> ::type> ::type> ()));
};
Ejemplo 2 (después)
template < typename T> struct s1 { ... };
template < typename T> struct s2 { ... };
template < typename T>
T declval();
struct s
{
template < typename T>
auto f(T t) - > decltype(t(declval< S1< typename S2< T> ::type> ::type> ()));
};
volatile
variables de miembro impiden los operadores de asignación y constructores definidos implícitamente
Las versiones anteriores del compilador permitían que se generaran automáticamente constructores para copiar o mover predeterminados y operadores de asignación para copiar o mover predeterminados para una clase con variables de miembro volatile
. Este comportamiento anterior era incorrecto y no se ajusta al estándar de C++. Ahora, el compilador considera que una clase que tiene variables de miembro volatile
tiene operadores de asignación y construcción no triviales, lo que impide que se generen de forma automática implementaciones predeterminadas de estos operadores. Cuando esta clase es miembro de una unión (o una unión anónima dentro de una clase), los constructores para copiar o mover, y los operadores de asignación para copiar o mover de la unión (o la clase que contiene la unión anónima) se definirán de forma implícita como eliminados. Intentar crear o copiar la unión (o la clase que contiene la unión anónima) sin definirlas explícitamente es un error y, en tal caso, el compilador genera el error de compilador C2280 como resultado.
error C2280: 'B::B(const B &)': attempting to reference a deleted function
Ejemplo (antes)
struct A
{
volatile int i;
volatile int j;
};
extern A* pa;
struct B
{
union
{
A a;
int i;
};
};
B b1{ *pa };
B b2(b1);
Ejemplo (después)
struct A
{
int i; int j;
};
extern volatile A* pa;
A getA()
{
A a;
a.i = pa - > i;
a.j = pa - > j;
return a;
}
struct B;
B b1{ GetA() };
B b2(b1);
Las funciones miembro estáticas no admiten calificadores cv.
Las versiones anteriores de Visual Studio 2015 permitían que las funciones miembro estáticas tuvieran calificadores cv. Este comportamiento se debe a una regresión en Visual Studio 2015 y Visual Studio 2015 Update 1, ya que Visual Studio 2013 y las versiones anteriores del compilador rechazan el código escrito de este modo. El comportamiento de Visual Studio 2015 y Visual Studio 2015 Update 1 no es correcto ni conforme con el estándar de C++. Visual Studio 2015 Update 2 rechaza el código escrito de este modo y genera el error de compilador C2511 en su lugar.
error C2511: 'void A::func(void) const': overloaded member function not found in 'A'
Ejemplo (antes)
struct A
{
static void func();
};
void A::func() const {}
Ejemplo (después)
struct A
{
static void func();
};
void A::func() {}
No se permite la declaración adelantada de la enumeración en el código de WinRT (solo afecta a /ZW
)
El código compilado para Windows Runtime (WinRT) no permite que los tipos enum
se declaren por adelantado, del mismo modo que cuando el código C++ administrado se compila para .NET Framework mediante el modificador de compilador /clr
. Este comportamiento garantiza que siempre se conozca el tamaño de una enumeración y que se pueda proyectar correctamente al sistema de tipos de WinRT. El compilador rechaza el código escrito de este modo y genera los errores de compilador C2599 y C3197.
error C2599: 'CustomEnum': the forward declaration of a WinRT enum is not allowed
error C3197: 'public': can only be used in definitions
Ejemplo (antes)
namespace A {
public enum class CustomEnum : int32;
}
namespace A {
public enum class CustomEnum : int32
{
Value1
};
}
public ref class Component sealed
{
public:
CustomEnum f()
{
return CustomEnum::Value1;
}
};
Ejemplo (después)
namespace A {
public enum class CustomEnum : int32
{
Value1
};
}
public ref class Component sealed
{
public:
CustomEnum f()
{
return CustomEnum::Value1;
}
};
El operador no miembro sobrecargado new y el operador delete no se pueden declarar como inline (nivel 1 (/W1
), activo de manera predeterminada)
Las versiones anteriores del compilador no generan ninguna advertencia cuando las funciones de operador new no miembro y de operador delete se declaraban como inline. El código escrito de este modo tiene un formato incorrecto (no se requiere ningún diagnóstico) y puede provocar problemas de memoria derivados de funciones operator new y operator delete no coincidentes (especialmente si se usan junto con una desasignación dimensionada) que puede resultar difícil de diagnosticar. Ahora, el compilador emite la advertencia C4595 para ayudarle a identificar el código escrito de este modo.
warning C4595: 'operator new': non-member operator new or delete functions may not be declared inline
Ejemplo (antes)
inline void* operator new(size_t sz)
{
...
}
Ejemplo (después)
void* operator new(size_t sz)
{
...
}
Corregir el código escrito de este modo puede requerir el traslado de las definiciones de operador fuera de un archivo de encabezado y al archivo de origen correspondiente.
std::is_convertable ahora detecta la asignación automática (biblioteca estándar)
Las versiones anteriores del rasgo de tipo std::is_convertable
no detectaban correctamente la asignación automática de un tipo de clase cuando el constructor de copias se había eliminado o era privado. Ahora, std::is_convertable<>::value
está establecido de forma correcta en false
cuando se aplica a un tipo de clase con un constructor de copias eliminado o privado.
No hay ningún diagnóstico del compilador asociado con este cambio.
Ejemplo
#include <type_traits>
class X1
{
public:
X1(const X1&) = delete;
};
class X2
{
private:
X2(const X2&);
};
static_assert(std::is_convertible<X1&, X1>::value, "BOOM");static_assert(std::is_convertible<X2&, X2>::value, "BOOM");
En versiones anteriores del compilador, las aserciones estáticas de la parte inferior de este ejemplo se aceptaban porque std::is_convertable<>::value
estaba establecido de forma incorrecta en true
. Ahora, std::is_convertable<>::value
se ha establecido de forma correcta en false
, lo que hace que se produzcan errores en las aserciones estáticas.
Los constructores de copias y movimiento triviales establecidos como valor predeterminado o eliminados respetan los especificadores de acceso
Las versiones anteriores del compilador no comprobaban el especificador de acceso de los constructores de copias y movimiento triviales establecidos como valor predeterminado o eliminados antes de permitir llamarlos. Este comportamiento anterior era incorrecto y no se ajusta al estándar de C++. En algunos casos, este comportamiento anterior ha creado un riesgo de generación de código no válido silencioso, lo que ha producido un comportamiento impredecible en tiempo de ejecución. Ahora, el compilador comprueba el especificador de acceso de los constructores de copias y movimiento triviales establecidos como valor predeterminado o eliminados para determinar si se pueden llamar y, en caso contrario, emite como resultado una advertencia del compilador C2248.
error C2248: 'S::S' cannot access private member declared in class 'S'
Ejemplo (antes)
class S {
public:
S() = default;
private:
S(const S&) = default;
};
void f(S);
int main()
{
S s;
f(s);
}
Ejemplo (después)
class S {
public:
S() = default;
private:
S(const S&) = default;
};
void f(const S&);
int main()
{
S s;
f(s);
}
Compatibilidad en desuso con código ATL con atributos (nivel 1 (/W1
), activo de manera predeterminada)
Las versiones anteriores del compilador admitían código ATL con atributos. Como parte de la fase siguiente para quitar la compatibilidad con código ATL con atributos que comenzó en Visual Studio 2008, el código ATL con atributos está en desuso. Ahora, el compilador emite la advertencia del compilador C4467 para ayudarle a identificar este tipo de código en desuso.
warning C4467: Usage of ATL attributes is deprecated
Si quiere seguir usando código ATL con atributos hasta que se quite la compatibilidad del compilador, puede deshabilitar esta advertencia si pasa los argumentos de la línea de comandos /Wv:18
o /wd:4467
al compilador, o bien agrega #pragma warning(disable:4467)
en el código fuente.
Ejemplo 1 (antes)
[uuid("594382D9-44B0-461A-8DE3-E06A3E73C5EB")]
class A {};
Ejemplo 1 (después)
__declspec(uuid("594382D9-44B0-461A-8DE3-E06A3E73C5EB")) A {};
A veces, necesitará o querrá crear un archivo IDL para evitar el uso de atributos ATL en desuso, como se muestra en el ejemplo de código siguiente
Ejemplo 2 (antes)
[emitidl];
[module(name = "Foo")];
[object, local, uuid("9e66a290-4365-11d2-a997-00c04fa37ddb")]
__interface ICustom {
HRESULT Custom([in] long l, [out, retval] long *pLong);
[local] HRESULT CustomLocal([in] long l, [out, retval] long *pLong);
};
[coclass, appobject, uuid("9e66a294-4365-11d2-a997-00c04fa37ddb")]
class CFoo : public ICustom
{
};
Primero, cree el archivo *.idl. Puede usar el archivo vc140.idl generado para obtener un archivo *.idl que contenga las interfaces y las anotaciones.
Después, agregue un paso MIDL a la compilación para asegurarse de que se generan las definiciones de interfaz de C++.
Ejemplo 2 de IDL (después)
import "docobj.idl";
[
object, local, uuid(9e66a290 - 4365 - 11d2 - a997 - 00c04fa37ddb)
]
interface ICustom : IUnknown {
HRESULT Custom([in] long l, [out, retval] long *pLong);
[local] HRESULT CustomLocal([in] long l, [out, retval] long *pLong);
};
[version(1.0), uuid(29079a2c - 5f3f - 3325 - 99a1 - 3ec9c40988bb)]
library Foo
{
importlib("stdole2.tlb");
importlib("olepro32.dll");
[
version(1.0),
appobject,uuid(9e66a294 - 4365 - 11d2 - a997 - 00c04fa37ddb)
]
coclass CFoo {
interface ICustom;
};
}
Luego, use ATL directamente en el archivo de implementación, como se muestra en el ejemplo de código siguiente.
Ejemplo 2 de implementación (después)
#include <idl.header.h>
#include <atlbase.h>
class ATL_NO_VTABLE CFooImpl :
public ICustom,
public ATL::CComObjectRootEx< CComMultiThreadModel>
{
public:
BEGIN_COM_MAP(CFooImpl)
COM_INTERFACE_ENTRY(ICustom)
END_COM_MAP()
};
Archivos de encabezado precompilado (PCH) y directivas de #include no coincidentes (solo afecta a /Wall
/WX
)
Las versiones anteriores del compilador aceptaban directivas #include
no coincidentes en archivos de código fuente entre compilaciones -Yc
y -Yu
cuando se usaban archivos de encabezado precompilado (PCH). El compilador ya no admite código escrito de este modo. Ahora, el compilador emite la advertencia del compilador CC4598 para ayudar a identificar las directivas #include
no coincidentes cuando se usan archivos PCH.
warning C4598: 'b.h': included header file specified for Ycc.h at position 2 does not match Yuc.h at that position
Ejemplo (antes):
X.cpp (-Ycc.h)
#include "a.h"
#include "b.h"
#include "c.h"
Z.cpp (-Yuc.h)
#include "b.h"
#include "a.h"
#include "c.h"
Ejemplo (después)
X.cpp (-Ycc.h)
#include "a.h"
#include "b.h"
#include "c.h"
Z.cpp (-Yuc.h)
#include "a.h"
#include "b.h"
#include "c.h"
Archivos de encabezado precompilado (PCH) y directorios de inclusión no coincidentes (solo afecta a /Wall
/WX
)
En las versiones anteriores del compilador se aceptaban argumentos de la línea de comandos de directorios de inclusión no coincidentes (-I
) en el compilador entre las compilaciones -Yc
y -Yu
cuando se usaban archivos de encabezado precompilado (PCH). El compilador ya no admite código escrito de este modo. Ahora, el compilador emite la advertencia del compilador CC4599 para ayudar a identificar los argumentos de la línea de comandos de directorios de inclusión no coincidentes (-I
) cuando se usan archivos PCH.
warning C4599: '-I..' : specified for Ycc.h at position 1 does not match Yuc.h at that position
Ejemplo (antes)
cl /c /Wall /Ycc.h -I.. X.cpp
cl /c /Wall /Yuc.h Z.cpp
Ejemplo (después)
cl /c /Wall /Ycc.h -I.. X.cpp
cl /c /Wall /Yuc.h -I.. Z.cpp
La palabra clave final genera ahora un error de símbolo sin resolver donde previamente se habría compilado:
struct S1 {
virtual void f() = 0;
};
struct S2 final : public S1 {
virtual void f();
};
int main(S2 *p)
{
p->f();
}
En versiones anteriores, el error no se producía porque la llamada era virtual
, aunque el programa sufriría un bloqueo en tiempo de ejecución. Ahora, se produce un error del vinculador porque se sabe que se trata de la clase final. En este ejemplo, para corregir el error, tendría que establecer vínculos con el objeto que contiene la definición de S2::f
.
Si usa funciones friend en los espacios de nombres, tendrá que volver a declarar la función friend antes de hacer referencia a ella o se producirá un error, ya que ahora el compilador se ajusta al estándar ISO C++. Por ejemplo, este ejemplo ya no se compila:
namespace NS {
class C {
void func(int);
friend void func(C* const) {}
};
void C::func(int) {
NS::func(this);
}
}
Para corregir este código, declare la función friend
:
namespace NS {
class C {
void func(int);
friend void func(C* const) {}
};
void func(C* const);
void C::func(int) {
NS::func(this);
}
El estándar de C++ no permite la especialización explícita en una clase. Aunque el Compilador de Microsoft C++ lo permite en ciertos casos, en otros como el del ejemplo siguiente se genera un error porque el compilador no considera que la segunda función sea una especialización de la primera.
template < int N>
class S {
public:
template void f(T& val);
template < > void f(char val);
};
template class S< 1>;
Para corregir este código, modifique la segunda función:
template <> void f(char& val);
En el ejemplo siguiente, el compilador ya no intenta eliminar la ambigüedad de las dos funciones y ahora notifica un error:
template< typename T> void Func(T* t = nullptr);
template< typename T> void Func(...);
int main() {
Func< int>();
}
Para corregir este código, clarifique la llamada:
template< typename T> void Func(T* t = nullptr);
template< typename T> void Func(...);
int main() {
Func< int>(nullptr);
}
Antes de que el compilador fuera conforme al estándar ISO C++11, el siguiente código se habría compilado y habría provocado que x
se resolviera en el tipo int
:
auto x = {0};
int y = x;
Ahora, este código resuelve x
en un tipo de std::initializer_list<int>
y produce un error en la siguiente línea que intenta asignar x
al tipo int
(no hay ninguna conversión de forma predeterminada). Para corregir este código, use int
para reemplazar auto
:
int x = {0};
int y = x;
Ya no se permite la inicialización de agregados cuando el tipo del valor de la derecha no coincide con el del valor de la izquierda que se está inicializando, y se produce un error porque el estándar ISO C++11 requiere que la inicialización uniforme funcione sin conversiones de restricción. Anteriormente, si había una conversión de restricción disponible, se emitía una advertencia del compilador (nivel 4) C4242 en lugar de un error.
int i = 0;
char c = {i};
Para corregir este código, agregue una conversión de restricción explícita:
int i = 0;
char c = {static_cast<char>(i)};
La inicialización siguiente ya no se permite:
void *p = {{0}};
Para corregir este código, use cualquiera de estas formas:
void *p = 0;
void *p = {0};
La búsqueda de nombres ha cambiado. El código siguiente se resuelve de forma diferente en el Compilador de Microsoft Visual C++ de Visual Studio 2012 y Visual Studio 2013:
enum class E1 { a };
enum class E2 { b };
int main()
{
typedef E2 E1;
E1::b;
}
En Visual Studio 2012, E1
en la expresión E1::b
se resuelve como ::E1
en el ámbito global. En Visual Studio 2013, E1
en la expresión E1::b
se resuelve como la definición typedef E2
de main()
y tiene el tipo ::E2
.
La disposición de los objetos ha cambiado. En x64, la disposición de los objetos de una clase puede cambiar con respecto a versiones anteriores. Si tiene una función virtual
, pero no tiene una clase base que tenga una función virtual
, el modelo de objetos del compilador inserta un puntero a una tabla de función virtual
después de la disposición de los miembros de datos. Esto significa que la disposición tal vez no sea óptima en todos los casos. En versiones anteriores, se ha intentado mejorar la disposición con una optimización de x64, pero como no ha funcionado correctamente en situaciones de código complejas, se ha eliminado en Visual Studio 2013. Por ejemplo, considere este código:
__declspec(align(16)) struct S1 {
};
struct S2 {
virtual ~S2();
void *p;
S1 s;
};
En Visual Studio 2013, el resultado de sizeof(S2)
en x64 es 48, pero en versiones anteriores se evalúa como 32. Para que esto se evalúe como 32 en el compilador de C++ de Visual Studio 2013 para x64, agregue una clase base ficticia que tenga una función virtual
:
__declspec(align(16)) struct S1 {
};
struct dummy {
virtual ~dummy() {}
};
struct S2 : public dummy {
virtual ~S2();
void *p;
S1 s;
};
Para buscar ubicaciones en el código que en una versión anterior se habrían intentado optimizar, use un compilador de esa versión junto con la opción del compilador /W3
y active la advertencia 4370. Por ejemplo:
#pragma warning(default:4370)
__declspec(align(16)) struct S1 {
};
struct S2 {
virtual ~S2();
void *p;
S1 s;
};
En versiones anteriores a Visual Studio 2013, este código genera este mensaje: "Advertencia C4370: "S2": el diseño de clase cambió desde una versión anterior del compilador debido a una mejora del empaquetado".
El compilador de x86 presenta el mismo problema de disposición en todas las versiones. Por ejemplo, si este código se compila para x86:
struct S {
virtual ~S();
int i;
double d;
};
El resultado de sizeof(S)
es 24. Pero se puede reducir a 16 si se usa la solución alternativa mencionada para x64:
struct dummy {
virtual ~dummy() {}
};
struct S : public dummy {
virtual ~S();
int i;
double d;
};
El Compilador de Microsoft Visual C++ de Visual Studio 2013 detecta las discordancias en _ITERATOR_DEBUG_LEVEL, función que se implementó en Visual Studio 2010, y las discordancias de la biblioteca en tiempo de ejecución. Estas discordancias se producen cuando se mezclan las opciones del compilador /MT
(versión estática), /MTd
(depuración estática), /MD
(versión dinámica) y /MDd
(depuración dinámica).
Se ha quitado la compatibilidad con Fusión (afxcomctl32.h); por lo tanto, se han quitado todos los métodos definidos en <afxcomctl32.h>
. Los archivos de encabezado <afxcomctl32.h>
y <afxcomctl32.inl>
se han eliminado.
Se ha cambiado el nombre de CDockablePane::RemoveFromDefaultPaneDividier
a CDockablePane::RemoveFromDefaultPaneDivider
.
Se ha cambiado la firma de CFileDialog::SetDefExt
para usar LPCTSTR; por tanto, se ven afectadas las compilaciones de Unicode.
Se han quitado categorías de seguimiento de ATL obsoletas.
Se ha cambiado la firma de CBasePane::MoveWindow
para tomar un const CRect
.
Se ha cambiado la firma de CMFCEditBrowseCtrl::EnableBrowseButton
.
Se ha quitado m_fntTabs
y m_fntTabsBold
de CMFCBaseTabCtrl
.
Se ha agregado un parámetro a los constructores CMFCRibbonStatusBarPane
. (Es un parámetro predeterminado y, por tanto, no requiere cambios en el código fuente).
Se ha agregado un parámetro al constructor CMFCRibbonCommandsListBox
. (Es un parámetro predeterminado y, por tanto, no requiere cambios en el código fuente).
Se ha quitado la API AFXTrackMouse
(y procesadores de temporizador relacionados). Use la API de Win32 TrackMouseEvent
en su lugar.
Se ha agregado un parámetro al constructor CFolderPickerDialog
. (Es un parámetro predeterminado y, por tanto, no requiere cambios en el código fuente).
Se ha cambiado el tamaño de la estructura de CFileStatus
: el miembro m_attribute
ha cambiado de BYTE a DWORD (para que coincida con el valor que ha devuelto GetFileAttributes
).
CRichEditCtrl
y CRichEditView
usan MSFTEDIT_CLASS (control RichEdit 4.1) en lugar de RICHEDIT_CLASS (control RichEdit 3.0) en las compilaciones de Unicode.
Se ha eliminado AFX_GLOBAL_DATA::IsWindowsThemingDrawParentBackground
porque siempre es TRUE en Windows Vista, Windows 7 y Windows 8.
Se ha eliminado AFX_GLOBAL_DATA::IsWindowsLayerSupportAvailable
porque siempre es TRUE en Windows Vista, Windows 7 y Windows 8.
Se ha eliminado AFX_GLOBAL_DATA::DwmExtendFrameIntoClientArea
. Llame a la API de Windows directamente en Windows Vista, Windows 7 y Windows 8.
Se ha eliminado AFX_GLOBAL_DATA::DwmDefWindowProc
. Llame a la API de Windows directamente en Windows Vista, Windows 7 y Windows 8.
Se ha cambiado el nombre de AFX_GLOBAL_DATA::DwmIsCompositionEnabled
a IsDwmCompositionEnabled
para eliminar el conflicto de nombres.
Se han cambiado los identificadores de varios temporizadores internos de MFC y se han movido las definiciones a afxres.h (AFX_TIMER_ID_ *).
Se ha cambiado la firma del método OnExitSizeMove
para que coincida con la macro ON_WM_EXITSIZEMOVE:
CFrameWndEx
CMDIFrameWndEx
CPaneFrameWnd
Se han cambiado el nombre y la firma de OnDWMCompositionChanged
para que coincidan con la macro ON_WM_DWMCOMPOSITIONCHANGED:
CFrameWndEx
CMDIFrameWndEx
CPaneFrameWnd
Se ha cambiado la firma del método OnMouseLeave
para que coincida con la macro ON_WM_MOUSELEAVE:
Se ha cambiado la firma de OnPowerBroadcast
para que coincida con la macro ON_WM_POWERBROADCAST:
CFrameWndEx
CMDIFrameWndEx
Se ha cambiado la firma de OnStyleChanged
para que coincida con la macro ON_WM_POWERBROADCAST:
CMFCListCtrl
CMFCStatusBar
Se ha cambiado el nombre del método interno FontFamalyProcFonts
a FontFamilyProcFonts
.
Se han quitado varios objetos de CString
estáticos globales para eliminar las pérdidas de memoria en algunas situaciones (reemplazado por #defines), y las siguientes variables de miembro de clase:
CKeyBoardManager::m_strDelimiter
CMFCPropertyGridProperty::m_strFormatChar
CMFCPropertyGridProperty::m_strFormatShort
CMFCPropertyGridProperty::m_strFormatLong
CMFCPropertyGridProperty::m_strFormatUShort
CMFCPropertyGridProperty::m_strFormatULong
CMFCPropertyGridProperty::m_strFormatFloat
CMFCPropertyGridProperty::m_strFormatDouble
CMFCToolBarImages::m_strPngResType
CMFCPropertyGridProperty::m_strFormat
Se ha cambiado la firma de CKeyboardManager::ShowAllAccelerators
y se ha quitado el parámetro de delimitador del acelerador.
Se ha agregado CPropertyPage::GetParentSheet
y en la clase CPropertyPage
, llámelo en lugar de GetParent
para obtener la ventana de hoja principal correcta, que puede ser el elemento primario o una ventana principal de CPropertyPage
. Es posible que tenga que cambiar el código para llamar a GetParentSheet
en lugar de GetParent
.
Se ha corregido #pragma warning(push) desequilibrado en ATLBASE.H, lo que ha provocado que las advertencias se deshabiliten de forma incorrecta. Esas advertencias están habilitadas correctamente después de que ATLBASE.H se haya analizado.
Se han movido métodos relacionados con D2D de AFX_GLOBAL_DATA a _AFX_D2D_STATE:
Se han quitado archivos de ATL*.CPP obsoletos de la carpeta \atlmfc\include\.
Se ha movido la inicialización afxGlobalData
a petición en lugar de en tiempo de inicialización de CRT para satisfacer los requisitos de DLLMain
.
Agrega el método RemoveButtonByIndex
a la clase CMFCOutlookBarPane
.
Se ha corregido CMFCCmdUsageCount::IsFreqeuntlyUsedCmd
a IsFrequentlyUsedCmd
.
Se han corregido varias instancias de RestoreOriginalstate
a RestoreOriginalState (CMFCToolBar, CMFCMenuBar, CMFCOutlookBarPane)
.
Se han quitado métodos no usados de CDockablePane
: SetCaptionStyle
, IsDrawCaption
, IsHideDisabledButtons
, GetRecentSiblingPaneInfo
y CanAdjustLayout
.
Se han quitado las variables de miembro estático de CDockablePane
m_bCaptionText
y m_bHideDisabledButtons
.
Agrega una invalidación del método DeleteString
a CMFCFontComboBox
.
Se han quitado métodos no usados de CPane
: GetMinLength
y IsLastPaneOnLastRow
.
Se ha cambiado el nombre de CPane::GetDockSiteRow(CDockingPanesRow *)
por CPane::SetDockSiteRow
.