Compartir a través de


Cambios importantes en Visual C++

Cuando se actualiza a una nueva versión del compilador de Visual C++, 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 importantes y 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.

En Visual C++, aunque está garantizado que la disposición de los objetos POD (datos antiguos de nivel) y las interfaces COM funcionen entre una versión y otra, otras modalidades de disposición de objetos (por ejemplo, en escenarios que implican creación de instancias de plantillas o herencia) están sujetas a cambios.

Para evitar errores en tiempo de ejecución que son difíciles de detectar y diagnosticar, recomendamos que nunca vincule estáticamente los binarios compilados con versiones diferentes del compilador.Además, cuando actualice un proyecto EXE o DLL, asegúrese de actualizar las bibliotecas a las que está vinculado.Si utiliza tipos CRT (Runtime de C) o STL (Biblioteca de plantillas estándar), no los pase entre los binarios (incluidos los archivos DLL) que se compilaron con versiones diferentes del compilador.Para obtener más información, vea Errores potenciales que pasan los objetos de CRT entre los límites de DLL.

También 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, vea Portabilidad en los límites de ABI (C++ moderno).

En el resto de este artículo se describen cambios importantes concretos en Visual C++ en Visual Studio 2013.

Compilador de Visual C++

  • 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 utiliza funciones friend en espacios de nombres, debe 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 a la norma ISO C++.Por ejemplo, este código ya no se compila:

    namespace NS {
        class C {
            void func(int);
            friend void func(C* const) {}
        };
    
        void C::func(int) { 
            NS::func(this);  // error
        }
    }
    

    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);  // conforming fix
    
        void C::func(int) { 
            NS::func(this); 
        }
    }
    
  • C++ Standard no permite la especialización explícita de una clase.Aunque Visual C++ lo permite en determinadas ocasiones, en casos como el del ejemplo siguiente, ahora 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,Visual C++ 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>(); // error
    } 
    

    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); // ok
    } 
    
  • Antes de que el compilador siguiera el 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 predeterminada). Para corregir este código, utilice int para reemplazar auto:

    int x = {0}; 
    
    int y = x; 
    
  • Ya no se permite la inicialización de agregado cuando el tipo del valor derecho no coincide con el tipo del valor izquierdo que se está inicializando, y se produce un error porque la norma ISO C++11 requiere que funcione la inicialización uniforme sin conversiones de restricción.Anteriormente, si había una conversión de restricción disponible, se emitía una advertencia C4242 en lugar de un error.

    int i = 0;
    char c = {i}; // error
    

    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; 
    // or 
    void *p = {0};
    
  • La búsqueda de nombres ha cambiado. El código siguiente se resuelve de manera diferente en Visual C++ en Visual Studio 2012 y Visual C++ en Visual Studio 2013:

    enum class E1 {a};
    enum class E2 {b};
    
    int main()
    {
        typedef E2 E1;
        E1::b;
    }
    

    En Visual C++ en Visual Studio 2012, E1 en la expresión E1::b se resuelve como ::E1 en el ámbito global.En Visual C++ 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 intentó mejorar la disposición con una optimización de x64, pero como no funcionó correctamente en situaciones de código complejas, se eliminó en Visual C++ 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 C++ en Visual Studio 2013, el resultado de sizeof(S2) en x64 es 48, pero en versiones anteriores, se evalúa como 32.Para que se evalúe como 32 en Visual C++ en 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 los compiladores de Visual C++ anteriores a Visual C++ en Visual Studio 2013, este código genera este mensaje:

    warning C4370: 'S2' : layout of class has changed from a previous version of the compiler due to better packing
    

    El compilador de x86 tiene el mismo problema de disposición en todas las versiones de Visual C++.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.Sin embargo, se puede reducir a 16 si se emplea la solución alternativa mencionada para x64:

    struct dummy { 
        virtual ~dummy() {} 
    };
    
    struct S : public dummy {
        virtual ~S();
        int i;
        double d;
    };
    

Bibliotecas de Visual C++

Biblioteca de plantillas estándar

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 C++ en Visual Studio 2013). Esta comprobación no detecta la combinación de archivos DLL ni detecta combinaciones que impliquen Visual C++ 2008 o versiones anteriores.

Visual C++ en Visual Studio 2013 detecta las discordancias en _ITERATOR_DEBUG_LEVEL, que se implementó en Visual C++ 2010, y las discordancias de RuntimeLibrary.Estas 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).Para obtener más información, vea Cambios importantes en Visual C++ 2012.

  • Si el código utiliza las plantillas de alias simuladas de la versión anterior, tiene que cambiarlo.Por ejemplo, en lugar de allocator_traits<A>::rebind_alloc<U>::other, tiene que especificar allocator_traits<A>::rebind_alloc<U>.Aunque ratio_add<R1, R2>::type ya no es necesario y ahora se recomienda ratio_add<R1, R2>, el primero seguirá compilándose porque ratio<N, D> es necesario para tener una definición de tipos “tipo” para una ratio reducida, que será el mismo tipo si ya se ha reducido.

  • Debe utilizar #include <algorithm> cuando llame a std::min() o std::max().

  • Si el código existente usa las enumeraciones con ámbito simuladas de la versión anterior (enumeraciones sin ámbito tradicionales incluidas en espacios de nombres), tendrá que cambiarlo.Por ejemplo, si incluía una referencia al tipo std::future_status::future_status, ahora tiene que especificar std::future_status.Sin embargo, la mayor parte del código no se ve afectado; por ejemplo, std::future_status::ready todavía se compila.

  • explicit operator bool() es más estricto que operator unspecified-bool-type().explicit operator bool() permite conversiones explícitas a bool (por ejemplo, para shared_ptr<X> sp, tanto static_cast<bool>(sp) como bool b(sp) son válidos) y "conversiones contextuales" de pruebas booleanas a bool (por ejemplo, if (sp), !sp, sp && whatever).Sin embargo, explicit operator bool() impide conversiones implícitas en bool, por lo que no puede especificar bool b = sp; y para un tipo de valor devuelto bool, no puede especificar return sp.

  • Ahora que se han implementado plantillas variádicas reales, _VARIADIC_MAX y las macros relacionadas no tienen ningún efecto. Si aún se mantiene la definición de _VARIADIC_MAX, sencillamente se omite. Si utilizó nuestra maquinaria de macros diseñada para admitir plantillas variádicas simuladas de cualquier otra forma, tendrá que cambiar el código.

  • Además de las palabras clave normales, los encabezados de STL prohíben ahora el uso de macros en las palabras clave contextuales “override” y “final”.

  • reference_wrapper/ref()/cref() ahora no permite el enlace con objetos temporales.

  • <random> ahora aplica estrictamente sus condiciones previas de tiempo de compilación.

  • Varios rasgos de tipo STL tienen la condición previa "T debe ser un tipo completo".Aunque el compilador aplique ahora esto de forma más estricta, no puede aplicarlo en todas las situaciones.(Dado que las infracciones de las condiciones previas de STL desencadenan un comportamiento no definido, la biblioteca de plantillas estándar no garantiza su cumplimiento).

  • STL no admite /clr:oldSyntax.

  • La especificación de C++11 para common_typecommon_type<int, int>::type tenía consecuencias inesperadas y no deseadas; en concreto, hacía que int&& devolviera .Por tanto, Visual C++ implementa la resolución propuesta para el problema 2141 del grupo de trabajo de biblioteca, que hace que common_type<int, int>::type devuelva int.

    Un efecto secundario de este cambio es que ya no funciona el caso de identidad (common_type<T> no siempre devuelve un tipo T).Este comportamiento se ajusta a la resolución propuesta, pero interrumpe cualquier código que dependiera del comportamiento anterior.

    Si necesita un tipo de rasgo de identidad, no utilice el elemento std::identity no estándar que se define en <type_traits> porque no funcionará para <void>.En su lugar, implemente un rasgo de tipo de identidad propio que se ajuste a sus necesidades.Por ejemplo:

    template <typename T> struct Identity {
        typedef T type;
    };
    

MFC y ATL

  • La biblioteca MFC MBCS ya no se incluye en Visual Studio debido a la popularidad de Unicode y a que el uso de MBCS se ha reducido significativamente.Este cambio también hace que MFC se adapte más al propio Windows SDK, ya que muchos de los nuevos controles y mensajes son solo Unicode.Sin embargo, si necesitar seguir usando la biblioteca MFC MBCS, puede descargarla del Centro de descarga de MSDN.El paquete redistribuible de Visual C++ todavía incluye esta biblioteca.

  • Se cambia la accesibilidad de la cinta de MFC.  En lugar de una arquitectura de una capa, ahora hay una arquitectura jerárquica. Puede seguir utilizando el comportamiento antiguo llamando a CRibbonBar::EnableSingleLevelAccessibilityMode().

  • Se quita el método CDatabase::GetConnect. Para mejorar la seguridad, ahora la cadena de conexión solo se almacena cifrada y se descifra solo si es necesario; no puede devolverse como texto sin formato.  La cadena se puede obtener mediante el método CDatabase::Dump.

  • La signatura de CWnd::OnPowerBroadcast se ha modificado. La signatura de este controlador de mensajes se ha modificado para tomar LPARAM como segundo parámetro.

  • Las signaturas se han modificado para alojar controladores de mensajes. Las listas de parámetros de las funciones siguientes se han modificado para utilizar los controladores de mensajes ON_WM_* que se han agregado:

    • CWnd::OnDisplayChange ha cambiado a (UINT, int, int) en lugar de a (WPARAM, LPARAM) para que pueda usarse la nueva macroON_WM_DISPLAYCHANGE en el mapa de mensajes.

    • CFrameWnd::OnDDEInitiate ha cambiado a (CWnd*, UINT, UNIT) en lugar de a (WPARAM, LPARAM) para que pueda usarse la nueva macroON_WM_DDE_INITIATE en el mapa de mensajes.

    • CFrameWnd::OnDDEExecute ha cambiado a (CWnd*, HANDLE) en lugar de a (WPARAM, LPARAM) para que pueda usarse la nueva macroON_WM_DDE_EXECUTE en el mapa de mensajes.

    • CFrameWnd::OnDDETerminate ha cambiado a (CWnd*) como parámetro en lugar de a (WPARAM, LPARAM) para que pueda usarse la nueva macro ON_WM_DDE_TERMINATE en el mapa de mensajes.

    • CMFCMaskedEdit::OnCut ha cambiado y ahora no tiene parámetros en lugar de cambiar a (WPARAM, LPARAM) para que la nueva macro ON_WM_CUT pueda usarse en el mapa de mensajes.

    • CMFCMaskedEdit::OnClear ha cambiado y ahora no tiene parámetros en lugar de cambiar a (WPARAM, LPARAM) para que la nueva macro ON_WM_CLEAR pueda usarse en el mapa de mensajes.

    • CMFCMaskedEdit::OnPaste ha cambiado y ahora no tiene parámetros en lugar de cambiar a (WPARAM, LPARAM) para que la nueva macro ON_WM_PASTE pueda usarse en el mapa de mensajes.

  • #ifdef se han quitado de los archivos de encabezado de MFC. Se han quitado numerosos #ifdefs de los archivos de encabezado de MFC relacionados con las versiones de Windows no compatibles (WINVER < 0x0501).

  • Se ha quitado ATL DLL (atl120.dll). ATL ahora se proporciona en forma de encabezados y como biblioteca estática (atls.lib).

  • Se ha quitado atlsd.lib, atlsn.lib y atlsnd.lib. Atls.lib ya no tiene código ni dependencias de juego de caracteres específicos para la depuración y el lanzamiento.Como funciona igual para Unicode y ANSI y para depuración y lanzamiento, solo se necesita una versión de la biblioteca.

  • Se ha quitado la herramienta de seguimiento ATL/MFC junto con ATL DLL, y se ha simplificado el mecanismo de seguimiento.Ahora, el constructor CTraceCategory toma un parámetro (el nombre de categoría) y las macros TRACE llaman a las funciones de creación de informes de depuración de CRT.

Vea también

Otros recursos

Introducción a Visual C++ en Visual Studio 2013