Wichtige Änderungen in Visual C++

Wenn Sie auf eine neue Version des Visual C++-Compilers aktualisieren, treten unter Umständen Kompilierungs- und/oder Laufzeitfehler im Code auf, der zuvor ordnungsgemäß kompiliert und ausgeführt wurde.Änderungen in der neuen Version, die solche Probleme verursachen, werden als wichtige Änderungen bezeichnet und werden in der Regel durch Änderungen im C++-Sprachenstandard, in den Funktionssignaturen oder im Layout von Objekten im Arbeitsspeicher erforderlich.

Obwohl in Visual C++ gewährleistet wird, dass das POD-Objektlayout (Plain Old Data) und COM-Schnittstellen von Version zu Version nicht geändert werden, unterliegen andere Objektlayouts Änderungen, z. B. in Szenarien, die Vererbung oder Vorlageninstanziierung einschließen.

Um Laufzeitfehler zu vermeiden, die schwer zu erkennen und zu diagnostizieren sind, wird empfohlen, keine statischen Links mit den Binärdateien zu erstellen, die mit verschiedenen Versionen des Compilers kompiliert wurden.Stellen Sie beim Aktualisieren eines EXE- oder DLL-Projekts außerdem sicher, die Bibliotheken zu aktualisieren, mit denen es verknüpft ist.Wenn Sie CRT- (C Runtime) oder STL-Typen (Standardvorlagenbibliothek) verwenden, übergeben Sie sie nicht zwischen Binärdateien (einschließlich DLLs), die mit verschiedenen Versionen des Compilers kompiliert wurden.Weitere Informationen finden Sie unter Potenzielle Fehler bei der Übergabe von CRT-Objekten über DLL-Grenzen.

Es wird außerdem empfohlen, niemals Code, der von einem bestimmten Layout abhängt, für ein Objekt zu schreiben, das keine COM-Schnittstelle oder kein POD-Objekt ist.Wenn Sie diesen Code schreiben, müssen Sie sicherstellen, dass er nach dem Upgrade funktioniert.Weitere Informationen finden Sie unter Portabilität an ABI-Grenzen (Modern C++).

Der restliche Artikel beschreibt spezifische wichtige Änderungen in Visual C++ in Visual Studio 2013.

Visual C++-Compiler

  • Das final-Schlüsselwort generiert jetzt einen nicht aufgelösten Symbolfehler, während es zuvor wie folgt kompiliert wurde:

    struct S1 {
        virtual void f() = 0;
    };
    
    struct S2 final : public S1 {
        virtual void f();
    };
    
    int main(S2 *p)
    {
        p->f();
    }
    

    In früheren Versionen wurde kein Fehler ausgegeben, da der Aufruf ein virtueller Aufruf war, das Programm würde jedoch zur Laufzeit abstürzen.Jetzt wird ein Linkerfehler ausgegeben, da die Klasse endgültig ist.In diesem Beispiel kann das Objekt, das die Definition von S2::f enthält, verknüpft werden, um den Fehler zu beheben.

  • Wenn Sie Friend-Funktionen in Namespaces verwenden, müssen Sie die Friend-Funktion erneut deklarieren, bevor Sie auf sie verweisen. Andernfalls wird ein Fehler ausgegeben, da der Compiler jetzt dem C++-Standard entspricht.Beispielsweise wird Folgendes nicht mehr kompiliert:

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

    Um diesen Code zu korrigieren, deklarieren Sie die Friend-Funktion:

    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); 
        }
    }
    
  • Der C++-Standard lässt keine explizite Spezialisierung in einer Klasse zu.Obwohl Visual C++ dies in manchen Fällen zulässt, wird im folgenden Beispiel jedoch ein Fehler generiert, da der Compiler die zweite Funktion nicht als Spezialisierung der ersten betrachtet.

    template <int N>
    class S { 
    public: 
        template  void f(T& val); 
        template <> void f(char val); 
    }; 
    
    template class S<1>; 
    

    Um diesen Code zu korrigieren, ändern Sie die zweite Funktion:

    template <> void f(char& val);
    
  • Im folgenden Beispiel können die beiden Funktionen mit Visual C++ nicht mehr eindeutig gemacht werden, und es wird jetzt ein Fehler ausgegeben:

    template<typename T> void Func(T* t = nullptr); 
    template<typename T> void Func(...); 
    
    int main() { 
        Func<int>(); // error
    } 
    

    Um diesen Code zu korrigieren, müssen Sie den Aufruf verdeutlichen:

    template<typename T> void Func(T* t = nullptr); 
    template<typename T> void Func(...); 
    
    int main() { 
        Func<int>(nullptr); // ok
    } 
    
  • Bevor der Compiler mit ISO C++11 kompatibel gemacht wurde, wurde der folgende Code kompiliert und führte dazu, dass x in den Typ int aufgelöst wurde:

    auto x = {0}; 
    
    int y = x; 
    

    Dieser Code löst nun x in den Typ std::initializer_list<int> auf und verursacht einen Fehler in der nächsten Zeile, wobei versucht wird, x dem Typ int zuzuweisen.(Es gibt keine standardmäßige Konvertierung.) Um diesen Code zu korrigieren, verwenden Sie int, um auto zu ersetzen:

    int x = {0}; 
    
    int y = x; 
    
  • Die Aggregatinitialisierung ist nicht mehr zulässig, wenn der Typ des rechten Werts nicht dem Typ des linken Werts entspricht, der initialisiert wird, und es wird ein Fehler ausgegeben, da der ISO C++11-Standard eine einheitliche Initialisierung erfordert, um ohne einschränkende Konvertierungen zu arbeiten.Bisher wurde die Warnung C4242 anstelle eines Fehlers ausgegeben, wenn eine einschränkende Konvertierung verfügbar war.

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

    Um diesen Code zu korrigieren, fügen Sie eine explizite einschränkende Konvertierung hinzu:

    int i = 0;
    char c = {static_cast<char>(i)};
    
  • Die folgende Initialisierung ist nicht mehr zulässig:

    void *p = {{0}};
    

    Um diesen Code zu korrigieren, verwenden Sie eines dieser Formulare:

    void *p = 0; 
    // or 
    void *p = {0};
    
  • Namenssuche wurde geändert. Der folgende Code wird in Visual C++ in Visual Studio 2012 und Visual C++ in Visual Studio 2013 unterschiedlich aufgelöst:

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

    In Visual C++ in Visual Studio 2012 wurde E1 im Ausdruck E1::b in ::E1 im globalen Gültigkeitsbereich aufgelöst.In Visual C++ in Visual Studio 2013 wird E1 im Ausdruck E1::b in die Definition typedef E2 in main() aufgelöst und weist den Typ ::E2 auf.

  • Das Objektlayout wurde geändert. Auf x64-Computern kann sich das Objektlayout einer Klasse gegenüber vorherigen Versionen möglicherweise ändern.Wenn es eine virtuelle Funktion aufweist, aber keine Basisklasse hat, die über eine virtuelle Funktion verfügt, fügt das Objektmodell des Compilers nach dem Datenmemberlayout einen Zeiger in eine virtuelle Funktionstabelle ein.Dies bedeutet, dass das Layout möglicherweise nicht in allen Fällen optimal ist.In vorherigen Versionen wurde mithilfe einer Optimierung für x64 versucht, das Layout zu verbessern. Da es jedoch in komplexen Codesituationen nicht richtig funktionierte, wurde es in Visual C++ in Visual Studio 2013 entfernt.Betrachten Sie beispielsweise folgenden Code:

    __declspec(align(16)) struct S1 {
    };
    
    struct S2 {
        virtual ~S2();
        void *p;
        S1 s;
    };
    

    In Visual C++ in Visual Studio 2013 ist das Ergebnis von sizeof(S2) auf x64-Computern 48, während es in vorherigen Versionen mit 32 ausgewertet wurde.Damit dieser Ausdruck in Visual C++ in Visual Studio 2013 für x64 mit 32 ausgewertet wird, fügen Sie eine Dummy-Basisklasse mit einer virtuellen Funktion hinzu:

    __declspec(align(16)) struct S1 {
    };
    
    struct dummy { 
        virtual ~dummy() {} 
    };
    struct S2 : public dummy {
        virtual ~S2();
        void *p;
        S1 s;
    };
    

    Um Stellen im Code zu suchen, die in einer früheren Version optimiert worden wären, verwenden Sie einen Compiler dieser Version zusammen mit der /W3-Compileroption und aktivieren Sie die Warnung 4370.Beispiel:

    #pragma warning(default:4370)
    
    __declspec(align(16)) struct S1 {
    };
    
    struct S2 {
        virtual ~S2();
        void *p;
        S1 s;
    };
    

    Auf Visual C++-Compilern vor Visual C++ in Visual Studio 2013 gibt dieser Code die folgende Meldung aus:

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

    Der x86-Compiler weist in allen Versionen von Visual C++ dasselbe Problem aufgrund eines nicht optimalen Layouts auf.Wenn dieser Code beispielsweise für x86 kompiliert wird:

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

    Das Ergebnis von sizeof(S) lautet 24.Dies kann jedoch auf 16 reduziert werden, wenn Sie die oben für x64 erwähnte Problemumgehung verwenden:

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

Visual C++-Bibliotheken

Standard Template Library

Um neue Optimierungen und Debuggingüberprüfungen zu aktivieren, unterbricht die Visual Studio-Implementierung der C++-Standardbibliothek absichtlich die binäre Kompatibilität von einer Version zur nächsten.Wenn die C++-Standardbibliothek verwendet wird, können Objektdateien und statische Bibliotheken, die unter Verwendung von verschiedenen Versionen kompiliert werden, nicht in einer Binärdatei (EXE oder DLL) vermischt werden, und C++-Standardbibliotheksobjekte können nicht zwischen Binärdateien übergeben werden, die mit verschiedenen Versionen kompiliert werden.Eine solche Kombination gibt Linkerfehler über _MSC_VER-Konflikte aus.(_MSC_VER ist das Makro, das die Hauptversion des Compilers enthält – z. B. 1800 für Visual C++ in Visual Studio 2013.) Diese Überprüfung kann weder eine gemischte DLL-Verwendung noch eine gemischte Verwendung erkennen, die Visual C++ 2008 oder früher betrifft.

Visual C++ in Visual Studio 2013 erkennt Konflikte im _ITERATOR_DEBUG_LEVEL, das in Visual C++ 2010 implementiert wurde, sowie RuntimeLibrary-Konflikte.Diese Konflikte treten auf, wenn die Compileroptionen /MT (statische Version), /MTd (statisches Debuggen), /MD (dynamische Version) und /MDd (dynamisches Debuggen) kombiniert werden.Weitere Informationen finden Sie unter Breaking Changes in Visual C++ 2012.

  • Wenn der Code die simulierten Aliasvorlagen der vorherigen Version bestätigt, müssen Sie ihn ändern.Beispielsweise müssen Sie statt allocator_traits<A>::rebind_alloc<U>::other jetzt allocator_traits<A>::rebind_alloc<U> verwenden.Obwohl ratio_add<R1, R2>::type nicht mehr notwendig ist und nun empfohlen wird, ratio_add<R1, R2> zu verwenden, wird der erste dennoch kompiliert, da für ratio<N, D> ein "Typ " typedef für ein reduziertes Verhältnis erforderlich ist. Dies ist derselbe Typ, wenn er bereits reduziert ist.

  • Sie müssen #include <algorithm> verwenden, wenn Sie std::min() oder std::max() aufrufen.

  • Wenn der vorhandene Code die simulierten bereichsbezogenen Enumerationen der vorherigen Version verwendet(herkömmliche nicht bereichsbezogene Enumerationen, die von Namespaces umschlossen sind), müssen Sie ihn ändern.Wenn Sie z. B. auf den Typ std::future_status::future_status verwiesen haben müssen Sie jetzt std::future_status verwenden.Die meisten Codes bleiben jedoch unberührt, std::future_status::ready wird beispielsweise weiterhin kompiliert.

  • explicit operator bool() ist strenger als operator unspecified-bool-type().explicit operator bool() ermöglicht explizite Konvertierungen in bool- z. B. bei shared_ptr<X> sp sind static_cast<bool>(sp) und bool b(sp) gültig und "kontextbedingte Konvertierungen", die auf den Booleschen Wert getestet werden können bool- z. B. if (sp), !sp, sp && whatever.Allerdings verbietet explicit operator bool() implizite Konvertierungen in bool, sodass Sie bool b = sp nicht verwenden können, und bei einem bestimmten bool-Rückgabetyp können Sie return sp nicht verwenden.

  • Da echte variadic-Vorlagen nun implementiert wurden, haben _VARIADIC_MAX und verknüpfte Makros keine Auswirkungen. Wenn Sie noch _VARIADIC_MAX definieren, wird es ignoriert. Wenn Sie die Makromaschinerie bestätigt haben, die dazu dienen sollten, simulierte variadic Vorlagen auf andere Weise zu unterstützen, müssen Sie den Code ändern.

  • Zusätzlich zu gewöhnlichen Schlüsselwörtern verbieten STL-Header jetzt das Erstellen von Makros zu den kontextbezogenen Schlüsselwörtern "override" und "final".

  • reference_wrapper/ref()/cref() verbieten jetzt, temporäre Objekte zu binden.

  • <random> erzwingt jetzt ausschließlich die Vorbedingungen der Kompilierzeit.

  • Verschiedene STL-Typmerkmale haben die Vorbedingung "T ist vollständiger Typ".Obwohl der Compiler dies jetzt strenger erzwingt, wird es möglicherweise nicht in allen Situationen erzwungen.(Da Verletzungen von STL-Vorbedingungen kein undefiniertes Verhalten auslösen, garantiert der Standard keine Erzwingung.)

  • STL unterstützt /clr:oldSyntax nicht.

  • Die C++11-Spezifizierung für common_type<> hatte unerwartete und unerwünschte Folgen. Insbesondere führte sie dazu, dass common_type<int, int>::typeint&& zurückgab.Daher implementiert Visual C++ die Vorgeschlagene Lösung für das Bibliotheks-Arbeitsgruppenproblem 2141, bei dem int von common_type<int, int>::type zurückgegeben wird.

    Als Nebeneffekt dieser Änderung funktioniert der Identitätsfall nicht mehr (common_type<T> ergibt nicht immer den Typ T).Dies entspricht der vorgeschlagenen Lösung, beeinträchtigt jedoch den Code, der auf dem vorherigen Verhalten beruhte.

    Wenn ein Identitätstypmerkmal erforderlich ist, verwenden Sie nicht std::identity, das kein Standard ist und in <type_traits> definiert ist, da dies bei <void> nicht funktioniert.Implementieren Sie stattdessen Ihr eigenes Identitätstypmerkmal, um Ihre Anforderungen zu erfüllen.Im Folgenden ein Beispiel:

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

MFC und ATL

  • Die MFC MBCS-Bibliothek ist nicht mehr in Visual Studio enthalten, da Unicode gängig ist und die Verwendung von MBCS erheblich reduziert wird.Durch diese Änderung orientiert sich MFC näher am Windows SDK, da viele der neuen Steuerelemente und der Meldungen ausschließlich in Unicode vorliegen.Wenn Sie die MFC-Bibliothek für MBCS jedoch weiterhin verwenden müssen, können Sie sie im MSDN-Download Center herunterladen.Diese Bibliothek ist weiterhin im Visual C++ Redistributable Package enthalten.

  • Zugriff auf das MFC-Menüband wurde geändert.  Anstelle einer Architektur mit einer Ebene gibt es jetzt eine hierarchische Architektur. Sie können das alte Verhalten weiterhin verwenden, indem Sie CRibbonBar::EnableSingleLevelAccessibilityMode() aufrufen.

  • CDatabase::GetConnect-Methode wurde entfernt. Um die Sicherheit zu verbessern, wird die Verbindungszeichenfolge jetzt verschlüsselt gespeichert und nur bei Bedarf entschlüsselt. Sie kann nicht als Nur Text zurückgegeben werden.  Die Zeichenfolge kann mit der CDatabase::Dump-Methode abgerufen werden.

  • Signatur von CWnd::OnPowerBroadcast wurde geändert. Die Signatur dieses Meldungshandlers wird geändert, um ein LPARAM als zweiten Parameter zu akzeptieren.

  • Signaturen werden zum anzupassen der Meldungshandler geändert. Die Parameterlisten der folgenden Funktionen wurden geändert, um neu hinzugefügte ON_WM_*-Meldungshandler zu verwenden:

    • CWnd::OnDisplayChange wurde anstatt (UINT, int, int) auf (WPARAM, LPARAM) geändert, damit das neue ON_WM_DISPLAYCHANGE-Makro in der Meldungszuordnung verwendet werden kann.

    • CFrameWnd::OnDDEInitiate wurde anstatt (CWnd*, UINT, UNIT) auf (WPARAM, LPARAM) geändert, damit das neue ON_WM_DDE_INITIATE-Makro in der Meldungszuordnung verwendet werden kann.

    • CFrameWnd::OnDDEExecute wurde anstatt (CWnd*, HANDLE) auf (WPARAM, LPARAM) geändert, damit das neue ON_WM_DDE_EXECUTE-Makro in der Meldungszuordnung verwendet werden kann.

    • CFrameWnd::OnDDETerminate wurde anstatt (CWnd*) als Parameter auf (WPARAM, LPARAM) geändert, damit das neue ON_WM_DDE_TERMINATE-Makro in der Meldungszuordnung verwendet werden kann.

    • CMFCMaskedEdit::OnCut wurde anstatt (WPARAM, LPARAM) auf keine Parameter geändert, damit das neue ON_WM_CUT-Makro in der Meldungszuordnung verwendet werden kann.

    • CMFCMaskedEdit::OnClear wurde anstatt (WPARAM, LPARAM) auf keine Parameter geändert, damit das neue ON_WM_CLEAR-Makro in der Meldungszuordnung verwendet werden kann.

    • CMFCMaskedEdit::OnPaste wurde anstatt (WPARAM, LPARAM) auf keine Parameter geändert, damit das neue ON_WM_PASTE-Makro in der Meldungszuordnung verwendet werden kann.

  • #ifdefs in den MFC-Headerdateien werden entfernt. Zahlreiche #ifdefs in den MFC-Headerdateien, die sich auf ältere, nicht unterstützte Versionen von Windows beziehen (WINVER < 0x0501), werden entfernt.

  • ATL DLL (atl120.dll) wurde entfernt. ATL wird jetzt als Header und als statische Bibliothek (atls.lib) bereitgestellt.

  • Atlsd.lib, atlsn.lib und atlsnd.lib wurden entfernt. Atls.lib weist keine(n) für Debuggen/Release spezifischen Zeichensatzabhängigkeiten oder Code mehr auf.Da die Funktionsweise für Unicode/ANSI und Debuggen/Release identisch ist, ist nur eine Version der Bibliothek erforderlich.

  • Das ATL-/MFC-Ablaufverfolgungstool wird zusammen mit ATL-DLL entfernt, und der Ablaufverfolgungsmechanismus wird vereinfacht.Der CTraceCategory-Konstruktor akzeptiert jetzt einen Parameter (den Kategorienamen), und mit den TRACE-Makros werden die CRT-Debugberichtsfunktionen aufgerufen.

Siehe auch

Weitere Ressourcen

Erste Schritte mit Visual C++ in Visual Studio 2013