Verbesserungen der C++-Konformität, Verhaltensänderungen und Fehlerbehebungen in Visual Studio 2017

Microsoft C/C++ in Visual Studio (MSVC) nimmt bei jedem Release Verbesserungen bei der Konformität mit Standards sowie bei Fehlerbehebungen vor. Dieser Artikel listet die Verbesserungen nach Hauptrelease und dann nach Versionen auf. Um direkt zu den Änderungen für eine bestimmte Version zu gelangen, verwenden Sie die unten stehende Liste In diesem Artikel.

In diesem Dokument werden die Änderungen in Visual Studio 2017 aufgelistet. Informationen zu den Änderungen in Visual Studio 2022 finden Sie unter Verbesserungen der C++-Konformität in Visual Studio 2022. Informationen zu den Änderungen in Visual Studio 2019 finden Sie unter Verbesserungen der C++-Konformität in Visual Studio 2019. Eine vollständige Liste der vorherigen Verbesserungen der Konformität finden Sie unter Visual C++: Neuerungen von 2003 bis 2015.

Verbesserungen der Konformität in Visual Studio 2017 RTW (Version 15.0)

Der MSVC-Compiler in Visual Studio 2017 unterstützt jetzt auch den generalisierten Spezifizierer constexpr sowie die Initialisierung nicht-statischer Datenmember (Non-Static Data Member Initialization, NSDMI) für Aggregate. Damit ist dieser Compiler für Features, die im C++14-Standard hinzugefügt wurden, vollständig. Dem Compiler fehlen jedoch noch einige Funktionen der C++11- und C++98-Standards. Informationen zum aktuellen Compilerstatus finden Sie unter Microsoft C/C++-Sprachkonformität.

C++11: Unterstützung für SFINAE für Ausdrücke in mehr Bibliotheken

Die Unterstützung für den SFINAE-Ausdruck wird im Compiler weiterhin verbessert. Dies ist für die Vorlagenargumentableitung und -ersetzung erforderlich, bei der die Ausdrücke decltype und constexpr möglicherweise als Vorlagenparameter angezeigt werden. Weitere Informationen finden Sie unter Expression SFINAE improvements in Visual Studio 2017 RC (Verbesserungen der SFINAE für Ausdrücke in Visual Studio 2017).

C++14: NSDMI für Aggregate

Ein Aggregat ist ein Array oder eine Klasse, für das/die Folgendes gilt: kein vom Benutzer bereitgestellter Konstruktor, keine nicht statischen Datenmember, die privat oder geschützt sind, keine Basisklassen und keine virtuellen Funktionen. Ab C++14 können Aggregate Memberinitialisierer enthalten. Weitere Informationen finden Sie unter Member initializers and aggregates (Memberinitialisierer und Aggregate).

C++14: Erweiterte constexpr

Als constexpr deklarierte Ausdrücke dürfen jetzt bestimmte Arten von Deklarationen, IF- und SWITCH-Anweisungen, Schleifenanweisungen und Mutationen von Objekten enthalten, deren Lebensdauer in der Auswertung von constexpr -Ausdrücken begonnen hat. Darüber hinaus ist es nicht mehr erforderlich, dass eine nicht statische Memberfunktion von constexpr implizit const ist. Weitere Informationen finden Sie unter Relaxing constraints on constexpr functions (Lockerung der Einschränkungen für constexpr-Funktionen).

C++17: Nicht ausführliche static_assert

der Meldungsparameter für static_assert ist optional. Weitere Informationen finden Sie unter N3928: Extending static_assert, v2 (Erweitern von static_assert, v2).

C++17: [[fallthrough]]-Attribut

Im /std:c++17 -Modus und höher kann das [[fallthrough]]-Attribut im Kontext von switch-Anweisungen als Hinweis für den Compiler verwendet werden, dass das Fall-Through-Verhalten vorgesehen ist. Dieses Attribut verhindert, dass der Compiler in solchen Fällen Warnungen ausgeben kann. Weitere Informationen finden Sie unter P0188R0 - Wording for [[fallthrough]] attribute.

Generalisierte bereichsbezogene for-Schleifen

Bereichsbezogene for-Schleifen erfordern nicht mehr, dass begin() und end() Objekte des gleichen Typs zurückgeben. Diese Änderung ermöglicht end() die Rückgabe eines Sentinels, wie von Bereichen in range-v3 verwendet, und die Einhaltung der abgeschlossenen, jedoch noch nicht veröffentlichten technischen Spezifikation zu Bereichen. Weitere Informationen finden Sie unter P0184R0 - Generalizing the Range-Based for Loop.

copy-list-Initialisierung

In Visual Studio 2017 werden ordnungsgemäß Compilerfehler im Zusammenhang mit der Objekterstellung bei Verwendung von Initialisiererlisten erzeugt. Diese Fehler wurden in Visual Studio 2015 nicht erfasst und konnten zu Abstürzen oder einem nicht definierten Laufzeitverhalten führen. Gemäß N4594 13.3.1.7p1 muss der Compiler in copy-list-initialization einen expliziten Konstruktor für die Überladungsauflösung berücksichtigen. Er muss jedoch einen Fehler auslösen, wenn diese bestimmte Überladung ausgewählt wird.

Die folgenden beiden Beispiele kompilieren in Visual Studio 2015, aber nicht in Visual Studio 2017.

struct A
{
    explicit A(int) {}
    A(double) {}
};

int main()
{
    A a1 = { 1 }; // error C3445: copy-list-initialization of 'A' cannot use an explicit constructor
    const A& a2 = { 1 }; // error C2440: 'initializing': cannot convert from 'int' to 'const A &'

}

Verwenden Sie direkte Initialisierung, um den Fehler zu korrigieren:

A a1{ 1 };
const A& a2{ 1 };

In Visual Studio 2015 behandelt der Compiler fälschlicherweise eine copy-list-Initialisierung auf die gleiche Weise wie eine normale Kopierinitialisierung. Er konvertiert nur die Konstruktoren für die Überladungsauflösung. Im folgenden Beispiel wählt Visual Studio 2015 MyInt(23). Visual Studio 2017 löst den Fehler ordnungsgemäß aus.

// From http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_closed.html#1228
struct MyStore {
    explicit MyStore(int initialCapacity);
};

struct MyInt {
    MyInt(int i);
};

struct Printer {
    void operator()(MyStore const& s);
    void operator()(MyInt const& i);
};

void f() {
    Printer p;
    p({ 23 }); // C3066: there are multiple ways that an object
        // of this type can be called with these arguments
}

Dieses Beispiel ähnelt dem vorherigen Beispiel, löst jedoch einen anderen Fehler aus. Es ist in Visual Studio 2015 erfolgreich. In Visual Studio 2017 schlägt es fehl mit C2668.

struct A {
    explicit A(int) {}
};

struct B {
    B(int) {}
};

void f(const A&) {}
void f(const B&) {}

int main()
{
    f({ 1 }); // error C2668: 'f': ambiguous call to overloaded function
}

Veraltete TypeDefs

Visual Studio 2017 gibt jetzt die richtige Warnung für veraltete TypeDefs aus, die in einer Klasse oder Struktur deklariert sind. In Visual Studio 2015 wird das folgende Beispiel ohne Warnungen kompiliert. In Visual Studio 2017 führt es zu C4996.

struct A
{
    // also for __declspec(deprecated)
    [[deprecated]] typedef int inttype;
};

int main()
{
    A::inttype a = 0; // C4996 'A::inttype': was declared deprecated
}

constexpr

Visual Studio 2017 löst ordnungsgemäß einen Fehler aus, wenn der linke Operand eines bedingten Auswertungsvorgangs in einem constexpr-Kontext ungültig ist. Der folgende Code wird in Visual Studio 2015 kompiliert. In Visual Studio 2017 führt er jedoch zu C3615:

template<int N>
struct array
{
    int size() const { return N; }
};

constexpr bool f(const array<1> &arr)
{
    return arr.size() == 10 || arr.size() == 11; // C3615 constexpr function 'f' cannot result in a constant expression
}

Deklarieren Sie die Funktion array::size() als constexpr, oder entfernen Sie den Qualifizierer constexpr aus f, um den Fehler zu beheben.

Klassentypen, die an variadic-Funktionen übergeben werden

In Visual Studio 2017 müssen Klassen oder Strukturen, die an eine variadic-Funktion übergeben werden (z. B printf), einfach kopierbar sein. Wenn solche Objekte übergeben werden, macht der Compiler eine bitweise Kopie und ruft keinen Konstruktor oder Destruktor auf.

#include <atomic>
#include <memory>
#include <stdio.h>

int main()
{
    std::atomic<int> i(0);
    printf("%i\n", i); // error C4839: non-standard use of class 'std::atomic<int>'
                        // as an argument to a variadic function.
                        // note: the constructor and destructor will not be called;
                        // a bitwise copy of the class will be passed as the argument
                        // error C2280: 'std::atomic<int>::atomic(const std::atomic<int> &)':
                        // attempting to reference a deleted function

    struct S {
        S(int i) : i(i) {}
        S(const S& other) : i(other.i) {}
        operator int() { return i; }
    private:
        int i;
    } s(0);
    printf("%i\n", s); // warning C4840 : non-portable use of class 'main::S'
                      // as an argument to a variadic function
}

Sie können eine Memberfunktion aufrufen, die einen einfachen kopierbaren Typ zurückgibt, um den Fehler zu beheben,

    std::atomic<int> i(0);
    printf("%i\n", i.load());

oder Sie verwenden eine statische Umwandlung, um das Objekt zu konvertieren, bevor es übergeben wird:

    struct S {/* as before */} s(0);
    printf("%i\n", static_cast<int>(s))

Für mit CString erstellte und verwaltete Zeichenfolgen sollte der bereitgestellte operator LPCTSTR() verwendet werden, um ein CString-Objekt in einen C-Zeiger umzuwandeln, der von der Formatzeichenfolge erwartet wird.

CString str1;
CString str2 = _T("hello!");
str1.Format(_T("%s"), static_cast<LPCTSTR>(str2));

CV-Qualifizierer in der Klassenkonstruktion

In Visual Studio 2015 ignoriert der Compiler beim Generieren eines Klassenobjekts über einen Konstruktoraufruf manchmal fälschlicherweise die CV-Qualifizierer. Dieses Problem kann potenziell zu einem Absturz oder zu unerwartetem Laufzeitverhalten führen. Das folgende Beispiel kompiliert in Visual Studio 2015, aber löst einen Compilerfehler in Visual Studio 2017 aus:

struct S
{
    S(int);
    operator int();
};

int i = (const S)0; // error C2440

Deklarieren Sie operator int() als const, um den Fehler zu beheben.

Zugriff auf qualifizierte Namen in Vorlagen überprüfen

In früheren Versionen des Compilers wurde der Zugriff auf qualifizierte Namen in bestimmten Kontexten von Vorlagen nicht überprüft. Dieses Problem kann das erwartete SFINAE-Verhalten behindern, bei dem die Ersetzung aufgrund der Nichterreichbarkeit des Namens erwartungsgemäß fehlschlägt. Dies konnte potenziell einen Absturz oder unerwartetes Verhalten zur Laufzeit auslösen, da der Compiler die falsche Überladung des Operators aufgerufen hat. In Visual Studio 2017 wird ein Compilerfehler ausgelöst. Der spezifische Fehler kann variieren, aber ein typischer Fehler ist C2672 („Keine übereinstimmende überladene Funktion gefunden“). Der folgende Code wird in Visual Studio 2015 kompiliert, löst jedoch in Visual Studio 2017 einen Fehler aus:

#include <type_traits>

template <class T> class S {
    typedef typename T type;
};

template <class T, std::enable_if<std::is_integral<typename S<T>::type>::value, T> * = 0>
bool f(T x);

int main()
{
    f(10); // C2672: No matching overloaded function found.
}

Fehlende Vorlagenargumentlisten

Bis Visual Studio 2015 wurde vom Compiler keine Diagnose für fehlende Vorlagenargumentlisten durchgeführt. Es wurde nicht bemerkt, wenn die fehlende Vorlage in einer Vorlagenparameterliste enthalten war, z. B. wenn ein Teil eines Standardvorlagenarguments oder ein Nichttyp-Vorlagenparameter fehlte. Dieses Problem kann zu unvorhersehbarem Verhalten führen, einschließlich des Absturzes des Compilers oder unerwartetem Laufzeitverhalten. Der folgende Code kompiliert in Visual Studio 2015, aber erzeugt in Visual Studio 2017 einen Fehler.

template <class T> class ListNode;
template <class T> using ListNodeMember = ListNode<T> T::*;
template <class T, ListNodeMember M> class ListHead; // C2955: 'ListNodeMember': use of alias
                                                     // template requires template argument list

// correct:  template <class T, ListNodeMember<T> M> class ListHead;

SFINAE für Ausdrücke

Um SFINAE für Ausdrücke zu unterstützen, analysiert der Compiler jetzt decltype-Argumente, wenn die Vorlagen deklariert, aber nicht instanziiert sind. Wenn also eine nicht abhängige Spezialisierung im decltype-Argument gefunden wird, wird sie bis zum Instanziierungszeitpunkt nicht zurückgestellt. Es wird sofort verarbeitet, und alle daraus resultierenden Fehler werden sofort diagnostiziert.

Das folgende Beispiel zeigt einen solchen Compilerfehler, der zum Zeitpunkt der Deklaration ausgelöst wird:

#include <utility>
template <class T, class ReturnT, class... ArgsT>
class IsCallable
{
public:
    struct BadType {};

    template <class U>
    static decltype(std::declval<T>()(std::declval<ArgsT>()...)) Test(int); //C2064. Should be declval<U>

    template <class U>
    static BadType Test(...);

    static constexpr bool value = std::is_convertible<decltype(Test<T>(0)), ReturnT>::value;
};

constexpr bool test1 = IsCallable<int(), int>::value;
static_assert(test1, "PASS1");
constexpr bool test2 = !IsCallable<int*, int>::value;
static_assert(test2, "PASS2");

In anonymen Namespaces deklarierte Klassen

Entsprechend dem C++-Standard verfügt eine Klasse, die innerhalb eines anonymen Namespace deklariert wird, über interne Verknüpfungen und kann daher nicht exportiert werden. In Visual Studio 2015 und früheren Versionen wurde diese Regel nicht durchgesetzt. In Visual Studio 2017 wird die Regel teilweise durchgesetzt. In Visual Studio 2017 führt das folgende Beispiel zu Fehler C2201:

struct __declspec(dllexport) S1 { virtual void f() {} };
  // C2201 const anonymous namespace::S1::vftable: must have external linkage
  // in order to be exported/imported.

Standardinitialisierer für Wertklassenmember (C++/CLI)

In Visual Studio 2015 und früher, ließ der Compiler einen Standardmember-Initialisierer für einen Member einer Wertklasse zu, ignorierte diesen aber. Standardinitialisierung einer Wertklasse initialisiert die Elemente immer auf null. Ein Standardkonstruktor ist nicht zulässig. In Visual Studio 2017 lösen Standardmember-Initialisierer einen Compilerfehler aus, wie im folgenden Beispiel gezeigt:

value struct V
{
    int i = 0; // error C3446: 'V::i': a default member initializer
               // isn't allowed for a member of a value class
};

Standardindexer (C++/CLI)

In Visual Studio 2015 und früher hat der Compiler in einigen Fällen fälschlicherweise eine Standardeigenschaft als einen Standardindexer kategorisiert. Es war möglich das Problem zu umzugehen, indem mithilfe des Bezeichners default auf die Eigenschaft zugegriffen wurde. Diese Lösung wurde problematisch, nachdem default als Schlüsselwort in C++11 eingeführt wurde. In Visual Studio 2017 wurden die Fehler, für die eine Problemumgehung erforderlich war, behoben. Der Compiler löst jetzt einen Fehler aus, wenn für den Zugriff auf die Standardeigenschaft für eine Klasse default verwendet wird.

//class1.cs

using System.Reflection;
using System.Runtime.InteropServices;

namespace ClassLibrary1
{
    [DefaultMember("Value")]
    public class Class1
    {
        public int Value
        {
            // using attribute on the return type triggers the compiler bug
            [return: MarshalAs(UnmanagedType.I4)]
            get;
        }
    }
    [DefaultMember("Value")]
    public class Class2
    {
        public int Value
        {
            get;
        }
    }
}

// code.cpp
#using "class1.dll"

void f(ClassLibrary1::Class1 ^r1, ClassLibrary1::Class2 ^r2)
{
       r1->Value; // error
       r1->default;
       r2->Value;
       r2->default; // error
}

In Visual Studio 2017 können Sie anhand ihres Namens auf beide Eigenschaften zugreifen:

#using "class1.dll"

void f(ClassLibrary1::Class1 ^r1, ClassLibrary1::Class2 ^r2)
{
       r1->Value;
       r2->Value;
}

Verbesserungen der Konformität in 15.3

constexpr-Lambdas

Lambdaausdrücke können jetzt in konstanten Ausdrücken verwendet werden. Weitere Informationen finden Sie unter constexpr-Lambdaausdrücke in C++.

if constexpr in Funktionsvorlagen

Eine Funktionsvorlage kann if constexpr-Anweisungen zum Branchen zur Kompilierzeit enthalten. Weitere Informationen finden Sie unter if constexpr-Anweisungen.

Auswahlanweisungen mit Initialisierern

Eine if-Anweisung kann einen Initialisierer enthalten, der im Blockbereich innerhalb der Anweisung selbst eine Variable einführt. Weitere Informationen finden Sie unter if-Anweisungen mit Initialisierer.

[[maybe_unused]]- und [[nodiscard]]-Attribute

Ein neues [[maybe_unused]]-Attribut schaltet Warnungen aus, wenn eine Entität nicht verwendet wird. Das [[nodiscard]]-Attribut generiert eine Warnung, wenn der Rückgabewert eines Funktionsaufrufs verworfen wird. Weitere Informationen finden Sie unter Attribute in C++.

Verwenden von Attributnamespaces ohne Wiederholung

Neue Syntax, um nur einen einzigen Namespacebezeichner in einer Attributliste zu erlauben. Weitere Informationen finden Sie unter Attribute in C++.

Strukturierte Bindungen

Es ist jetzt möglich, einen Wert mit individuellen Namen für die verschiedenen Komponenten in einer einzigen Anweisung zu speichern. Dies gilt, wenn der Wert ein Array, std::tuple oder std::pair ist oder nur öffentliche, nicht statische Datenmember enthält. Weitere Informationen finden Sie unter P0144R0 - Structured Bindings und Returning multiple values from a function (Zurückgeben von mehreren Werten aus einer Funktion).

Erstellungsregeln für enum class-Klassenwerte

Es gibt nun eine implizite Konvertierung für Enumerationen mit eigenem Gültigkeitsbereich, die nicht einschränkend ist. Es erfolgt eine Konvertierung vom zugrunde liegenden Typ einer Enumeration mit eigenem Gültigkeitsbereich in die Enumeration selbst. Diese Konvertierung ist verfügbar, wenn in der Definition kein Enumerator eingeführt wird und in der Quelle eine Listeninitialisierungsyntax verwendet wird. Weitere Informationen finden Sie unter P0138R2 - Construction Rules for enum class Values und Enumerationen.

Erfassen von *this nach Wert

Das *this-Objekt in einem Lambdaausdruck kann jetzt anhand des Werts erfasst werden. Diese Änderung ermöglicht Szenarios in denen der Lambdaausdruck in parallelen und asynchronen Vorgängen aufgerufen wird, insbesondere in neueren Computerarchitekturen. Weitere Informationen finden Sie unter P0018R3 - Lambda Capture of *this by Value as [=,*this].

Entfernen von operator++ für bool

operator++ wird nicht mehr in bool-Typen unterstützt. Weitere Informationen finden Sie unter P0002R1 - Remove Deprecated operator++(bool).

Entfernen des veralteten Schlüsselworts register

Das Schlüsselwort register, das als veraltet gekennzeichnet wurde (und vom Visual C++-Compiler ignoriert wurde), wurde jetzt aus der Sprache entfernt. Weitere Informationen finden Sie unter P0001R1 - Remove Deprecated Use of the register Keyword.

Aufrufe gelöschter Membervorlagen

In früheren Versionen von Visual Studio gab der Compiler mitunter keine Fehlermeldung bei falsch formatierten Aufrufen einer gelöschten Membervorlage zurück. Durch diese Aufrufe wurden zur Laufzeit möglicherweise Abstürze verursacht. Der folgende Code führt jetzt zu Fehler C2280:

template<typename T>
struct S {
   template<typename U> static int f() = delete;
};

void g()
{
   decltype(S<int>::f<int>()) i; // this should fail with
// C2280: 'int S<int>::f<int>(void)': attempting to reference a deleted function
}

Um den Fehler zu beheben, deklarieren Sie i als int.

Voraussetzungsprüfungen für Typmerkmale

Visual Studio 2017 Version 15.3 verbessert Voraussetzungsprüfungen für Typmerkmale so, dass der Standard strenger befolgt wird. Eine solche Prüfung erfolgt für „assignable“. Der folgende Code führt in Visual Studio 2017, Version 15.3, zu Fehler C2139:

struct S;
enum E;

static_assert(!__is_assignable(S, S), "fail"); // C2139 in 15.3
static_assert(__is_convertible_to(E, E), "fail"); // C2139 in 15.3

Neue Compilerwarnung und CLR-Prüfungen für Marshalling von nativ zu verwaltet

Aufrufe nativer Funktionen aus verwalteten Funktionen erfordern Marshalling. Die CLR führt das Marshalling aus, versteht aber nicht die C++-Semantik. Wenn Sie ein natives Objekt nach Wert übergeben, ruft die CLR entweder den Kopierkonstruktor des Objekts auf oder verwendet BitBlt, was zu einem nicht definiertem Verhalten zur Laufzeit führt.

Jetzt gibt der Compiler eine Warnung aus, wenn er diesen Fehler zur Kompilierzeit findet. Ein natives Objekt mit gelöschtem Kopierkonstruktor wird zwischen einer nativen und einer verwalteten Grenze nach Wert übergeben. In den Fällen, in denen dem Compiler zur Kompilierzeit keine Informationen vorliegen, fügt er eine Laufzeitprüfung hinzu, sodass das Programm sofort std::terminate aufruft, sobald ein falsch formatiertes Marshalling erfolgt. Der folgende Code führt in Visual Studio 2017 Version 15.3 zu Warnung C4606:

class A
{
public:
   A() : p_(new int) {}
   ~A() { delete p_; }

   A(A const &) = delete;
   A(A &&rhs) {
   p_ = rhs.p_;
}

private:
   int *p_;
};

#pragma unmanaged

void f(A a)
{
}

#pragma managed

int main()
{
    // This call from managed to native requires marshaling. The CLR doesn't
    // understand C++ and uses BitBlt, which results in a double-free later.
    f(A()); // C4606 'A': passing argument by value across native and managed
    // boundary requires valid copy constructor. Otherwise, the runtime
    // behavior is undefined.`
}

Um den Fehler zu beheben, entfernen Sie die Direktive #pragma managed, um den Aufrufer als nativ zu markieren und Marshalling zu vermeiden.

Warnung zu experimenteller API für WinRT

WinRT-APIs, die zu Experimentier- und Feedbackzwecken veröffentlicht werden, werden mit Windows.Foundation.Metadata.ExperimentalAttribute versehen. In Visual Studio 2017 Version 15.3 führt der Compiler zu Warnung C4698 für dieses Attribut. Einige APIs in früheren Versionen des Windows SDK wurden bereits mit dem Attribut markiert, sodass Aufrufe dieser APIs jetzt diese Compilerwarnung auslösen. In neueren Windows SDKs wurde das Attribut in allen ausgelieferten Typen entfernt. Wenn Sie ein älteres SDK verwenden, müssen Sie diese Warnungen für alle Aufrufe an ausgelieferte Typen unterdrücken.

Mit dem folgenden Code wird die Warnung C4698 generiert:

Windows::Storage::IApplicationDataStatics2::GetForUserAsync(); // C4698
// 'Windows::Storage::IApplicationDataStatics2::GetForUserAsync' is for
// evaluation purposes only and is subject to change or removal in future updates

Fügen Sie #pragma hinzu, um die Warnung zu deaktivieren:

#pragma warning(push)
#pragma warning(disable:4698)

Windows::Storage::IApplicationDataStatics2::GetForUserAsync();

#pragma warning(pop)

Out-of-Line-Definition einer Vorlagenmemberfunktion

Visual Studio 2017 Version 15.3 generiert einen Fehler für Out-of-Line-Definitionen einer Vorlagenmemberfunktion, die in der Klasse nicht deklariert wurde. Der folgende Code führt nun zu Fehler C2039:

struct S {};

template <typename T>
void S::f(T t) {} // C2039: 'f': is not a member of 'S'

Um den Fehler zu beheben, fügen Sie der Klasse eine Deklaration hinzu:

struct S {
    template <typename T>
    void f(T t);
};
template <typename T>
void S::f(T t) {}

Es wird versucht, die Adresse eines this-Zeigers zu verwenden

In C++ ist this ein prvalue des Typzeigers auf X. Sie können nicht die Adresse von this verwenden oder sie an einen lvalue-Verweis binden. In früheren Versionen von Visual Studio konnte es Ihnen der Compiler ermöglichen, diese Einschränkung mithilfe einer Umwandlung zu umgehen. In Visual Studio 2017 Version 15.3 generiert der Compiler Fehler C2664.

Konvertierung in eine Basisklasse, auf die nicht zugegriffen werden kann

Visual Studio 2017 Version 15.3 generiert einen Fehler, wenn Sie versuchen, einen Typ in eine Basisklasse zu konvertieren, auf die nicht zugegriffen werden kann. Der folgende Code ist falsch formatiert und kann möglicherweise einen Absturz während der Laufzeit verursachen. Im Kompilierer führt dieser Code nun zu C2243:

#include <memory>

class B { };
class D : B { }; // C2243: 'type cast': conversion from 'D *' to 'B *' exists, but is inaccessible

void f()
{
   std::unique_ptr<B>(new D());
}

Standardargumente sind in Out-of-Line-Definitionen von Memberfunktionen nicht zulässig

Standardargumente sind in Out-of-Line-Definitionen von Memberfunktionen in Vorlagenklassen nicht zulässig. Der Compiler gibt bei Verwendung von /permissive eine Warnung und unter /permissive- einen schwerwiegenden Fehler aus.

In früheren Versionen von Visual Studio konnte der folgende falsch formatierte Code einen Absturz zur Laufzeit verursachen. In Visual Studio 2017 Version 15.3 wird die Warnung C5037 generiert:

template <typename T>
struct A {
    T f(T t, bool b = false);
};

template <typename T>
T A<T>::f(T t, bool b = false) // C5037: 'A<T>::f': an out-of-line definition of a member of a class template cannot have default arguments
{
    // ...
}

Entfernen Sie das Standardargument = false, um den Fehler zu beheben.

Verwenden von offsetof mit zusammengesetztem Memberkennzeichner

In Visual Studio 2017 Version 15.3 führt das Verwenden von offsetof(T, m), wobei m ein „zusammengesetzter Memberkennzeichner“ ist, zu einer Warnung, wenn die Kompilierung mit der Option /Wall erfolgt. Der folgende Code ist falsch formatiert und kann möglicherweise zur Laufzeit einen Absturz verursachen. In Visual Studio 2017 Version 15.3 führt dies zur Warnung C4841:

struct A {
   int arr[10];
};

// warning C4841: non-standard extension used: compound member designator used in offsetof
constexpr auto off = offsetof(A, arr[2]);

Um den Code zu korrigieren, deaktivieren Sie die Warnung mit einem Pragma, oder ändern Sie den Code so, dass offsetof nicht verwendet wird:

#pragma warning(push)
#pragma warning(disable: 4841)
constexpr auto off = offsetof(A, arr[2]);
#pragma warning(pop)

Verwenden von offsetof mit statischem Datenmember oder mit Memberfunktion

In Visual Studio 2017 Version 15.3 führt das Verwenden von offsetof(T, m) zu einem Fehler, wenn m ein statischer Datenmember oder eine Memberfunktion ist. Der folgende Code führt zu Fehler C4597:

#include <cstddef>

struct A {
   int ten() { return 10; }
   static constexpr int two = 2;
};

constexpr auto off = offsetof(A, ten);  // C4597: undefined behavior: offsetof applied to member function 'A::ten'
constexpr auto off2 = offsetof(A, two); // C4597: undefined behavior: offsetof applied to static data member 'A::two'

Dieser Code ist falsch formatiert und kann möglicherweise zur Laufzeit einen Absturz verursachen. Um den Fehler zu beheben, ändern Sie den Code so, dass das nicht definierte Verhalten nicht mehr aufgerufen wird. Dies ist nicht portierbarer Code, der vom C++-Standard nicht zugelassen ist.

Neue Warnung zu __declspec-Attributen

In Visual Studio 2017 Version 15.3 ignoriert der Compiler Attribute nicht mehr, wenn __declspec(...) vor der externen Verknüpfungsspezifikation extern "C" angewendet wird. Zuvor hat der Compiler das Attribut ignoriert, was zu Problemen zur Laufzeit führen konnte. Bei Festlegen der Optionen /Wall und /WX führt der folgende Code zur Warnung C4768:

__declspec(noinline) extern "C" HRESULT __stdcall // C4768: __declspec attributes before linkage specification are ignored

Um die Warnung zu korrigieren, fügen Sie zuerst extern "C" hinzu:

extern "C" __declspec(noinline) HRESULT __stdcall

Diese Warnung ist in Visual Studio 2017 Version 15.3 standardmäßig deaktiviert und wirkt sich nur auf den mit /Wall/WX kompilierten Code aus. Ab Visual Studio 2017 Version 15.5 ist sie standardmäßig als Warnung der Stufe 3 aktiviert.

decltype und Aufrufe gelöschter Destruktoren

In früheren Versionen von Visual Studio erkannte der Compiler nicht, wenn ein Aufruf eines gelöschten Destruktors im Kontext des Ausdrucks erfolgte, der decltype zugeordnet war. Der folgende Code führt in Visual Studio 2017 Version 15.3 zu Fehler C2280:

template<typename T>
struct A
{
   ~A() = delete;
};

template<typename T>
auto f() -> A<T>;

template<typename T>
auto g(T) -> decltype((f<T>()));

void h()
{
   g(42); // C2280: 'A<T>::~A(void)': attempting to reference a deleted function
}

Nicht initialisierte „const“-Variablen

Das Visual Studio 2017 RTW-Release wies eine Regression auf, bei der der C++-Compiler keine Diagnose für eine nicht initialisierte const -Variable ausgab. Diese Regression wurde in Visual Studio 2017 Version 15.3 behoben. Der folgende Code führt jetzt zu Warnung C4132:

const int Value; // C4132: 'Value': const object should be initialized

Um den Fehler zu beheben, weisen Sie Value einen Wert zu.

Leere Deklarationen

Visual Studio 2017 Version 15.3 warnt bei leeren Deklarationen jetzt für alle Typen und nicht nur für integrierte Typen. Der folgende Code führt jetzt zur Warnung C4091 auf Stufe 2 für alle vier Deklarationen:

struct A {};
template <typename> struct B {};
enum C { c1, c2, c3 };

int;    // warning C4091 : '' : ignored on left of 'int' when no variable is declared
A;      // warning C4091 : '' : ignored on left of 'main::A' when no variable is declared
B<int>; // warning C4091 : '' : ignored on left of 'B<int>' when no variable is declared
C;      // warning C4091 : '' : ignored on left of 'C' when no variable is declared

Um die Warnungen zu entfernen, können Sie die leeren Deklarationen auskommentieren oder entfernen. In Fällen, in denen das nicht benannte Objekt einen Nebeneffekt haben soll (z. B. RAII), sollte es mit einem Namen versehen werden.

Die Warnung wird bei Verwendung von /Wv:18 ausgeschlossen und ist auf der Warnstufe W2 standardmäßig aktiviert.

std::is_convertible für Arraytypen

Frühere Versionen des Compilers haben zu falschen Ergebnissen für std::is_convertible für Arraytypen geführt. Daher mussten Bibliotheksautoren bei der Verwendung der Typeigenschaft std::is_convertible<...> besondere Schreibweisen für den Microsoft C++-Compiler anwenden. Im folgenden Beispiel sind die statischen Assertionen in früheren Versionen von Visual Studio erfolgreich, jedoch nicht in Visual Studio 2017 Version 15.3:

#include <type_traits>

using Array = char[1];

static_assert(std::is_convertible<Array, Array>::value);
static_assert(std::is_convertible<const Array, const Array>::value, "");
static_assert(std::is_convertible<Array&, Array>::value, "");
static_assert(std::is_convertible<Array, Array&>::value, "");

std::is_convertible<From, To> wird berechnet, indem überprüft wird, ob eine imaginäre Funktionsdefinition wohlgeformt ist:

   To test() { return std::declval<From>(); }

Private Destruktoren und std::is_constructible

Frühere Versionen des Compilers ignorierten bei der Entscheidung über das Ergebnis von std::is_constructible, ob ein Destruktor privat war. Dies wird nun berücksichtigt. Im folgenden Beispiel sind die statischen Assertionen in früheren Versionen von Visual Studio erfolgreich, jedoch nicht in Visual Studio 2017 Version 15.3:

#include <type_traits>

class PrivateDtor {
   PrivateDtor(int) { }
private:
   ~PrivateDtor() { }
};

// This assertion used to succeed. It now correctly fails.
static_assert(std::is_constructible<PrivateDtor, int>::value);

Private Destruktoren führen dazu, dass ein Typ nicht konstruierbar ist. std::is_constructible<T, Args...> wird berechnet, als würde die folgende Deklaration geschrieben:

   T obj(std::declval<Args>()...)

Dieser Aufruf impliziert einen Destruktoraufruf.

C2668: Mehrdeutige Überladungsauflösung

Frühere Versionen des Compilers konnten manchmal keine Mehrdeutigkeiten erkennen, wenn durch die Verwendung von sowohl Deklarationen als auch argumentabhängigen Lookups mehrere Kandidaten gefunden wurden. Dieser Fehler kann zum Auswählen der falschen Überladung und zu unerwartetem Laufzeitverhalten führen. Das folgende Beispiel führt in Visual Studio 2017 Version 15.3 ordnungsgemäß zu C2668:

namespace N {
   template<class T>
   void f(T&, T&);

   template<class T>
   void f();
}

template<class T>
void f(T&, T&);

struct S {};
void f()
{
   using N::f;

   S s1, s2;
   f(s1, s2); // C2668: 'f': ambiguous call to overloaded function
}

Um den Code zu korrigieren, entfernen Sie die Anweisung N::f, wenn Sie ::f() aufrufen wollten.

C2660: Lokale Funktionsdeklarationen und argumentabhängiges Lookup

Lokale Funktionsdeklarationen verbergen die Funktitonsdeklaration im umschließenden Bereich und deaktivieren das argumentabhängige Lookup. In früheren Versionen des Compilers wurde in diesem Fall immer eine argumentabhängige Suche durchgeführt. Dies konnte zu unerwartetem Laufzeitverhalten führen, wenn der Compiler die falsche Überladung auswählte. Der Fehler liegt in der Regel in einer falschen Signatur der lokalen Funktionsdeklaration. Das folgende Beispiel führt in Visual Studio 2017 Version 15.3 ordnungsgemäß zu C2660:

struct S {};
void f(S, int);

void g()
{
   void f(S); // C2660 'f': function does not take 2 arguments:
   // or void f(S, int);
   S s;
   f(s, 0);
}

Um das Problem zu beheben, ändern Sie entweder die Signatur f(S), oder entfernen Sie diese.

C5038: Reihenfolge der Initialisierung in Initialisiererlisten

Klassenmember werden in der Reihenfolge initialisiert, in der sie deklariert sind, nicht in der Reihenfolge, in der Sie in Initialisiererlisten angezeigt werden. Frühere Versionen des Compilers haben keine Warnung ausgegeben, wenn sich die Reihenfolge der Initialisiererliste von der Deklarationsreihenfolge unterschied. Dieses Problem konnte zu undefiniertem Laufzeitverhalten führen, wenn die Initialisierung eines Members von einem anderen Member in der Liste abhing, der bereits initialisiert wurde. Das folgende Beispiel führt in Visual Studio 2017 Version 15.3 (mit /Wall) zu Warnung C5038:

struct A
{    // Initialized in reverse, y reused
    A(int a) : y(a), x(y) {} // C5038: data member 'A::y' will be initialized after data member 'A::x'
    int x;
    int y;
};

Um das Problem zu beheben, ordnen Sie die Initialisiererliste so an, dass die Reihenfolge mit der Reihenfolge der Deklarationen übereinstimmt. Eine ähnliche Warnung wird ausgelöst, wenn ein oder beide Initialisierer auf Member der Basisklasse verweisen.

Diese Warnung ist standardmäßig deaktiviert und wirkt sich nur auf mit /Wall kompilierten Code aus.

Verbesserungen der Konformität in 15.5

Features, die mit [14] markiert sind, stehen selbst im /std:c++14-Modus ohne Bedingungen zur Verfügung.

Neue Compilerschalter für extern constexpr

In früheren Versionen von Visual Studio hat der Compiler immer eine interne constexpr -Variablenbindung ausgegeben, selbst wenn die Variable als extern markiert wurde. In Visual Studio 2017 Version 15.5 ermöglicht ein neuer Compilerschalter (/Zc:externConstexpr) das richtige standardkonforme Verhalten. Weitere Informationen finden Sie unter extern constexpr-Bindung.

Entfernen dynamischer Ausnahmespezifikationen

P0003R5 Dynamische Ausnahmespezifikationen sind in C++11 veraltet. Das Feature wird aus C ++ 17 entfernt, aber die (immer noch) veraltete throw()-Spezifikation wird strikt als Alias für noexcept(true) beibehalten. Weitere Informationen finden Sie unter Entfernen der dynamischen Ausnahmespezifikation und noexcept.

not_fn()

P0005R4not_fn ersetzt not1 und not2.

Umformulieren von enable_shared_from_this

P0033R1enable_shared_from_this wurde in C++11 hinzugefügt. Der C++17-Standard aktualisiert die Spezifikation, um bestimmte Ausnahmefälle besser zu verarbeiten. [14]

Splice-Zuordnungen und -Sätze

P0083R3 Dieses Feature ermöglicht das Extrahieren von Knoten aus assoziativen Containern (z.B. map, set, unordered_map, unordered_set), die dann geändert und wieder in den gleichen Container oder einen anderen Container eingefügt werden können, der den gleichen Knotentyp verwendet. (Ein häufiger Anwendungsfall besteht darin, einen Knoten aus einer std::map zu extrahieren, den Schlüssel zu ändern und ihn dann erneut einzufügen.)

Veraltete rudimentäre Bibliotheksteile

P0174R2 Einige Features der C++-Standardbibliothek wurden im Laufe der Jahre durch neuere Features ersetzt, oder es hat sich herausgestellt, dass sie nicht sehr nützlich oder sogar problematisch sind. Diese Funktionen werden sind in C++17 offiziell als veraltet eingestuft.

Entfernen der Zuweisungsunterstützung in std::function

P0302R1 Vor C++17 besaß die Klassenvorlage std::function mehrere Konstruktoren, die ein Zuweisungsargument angenommen haben. Allerdings war die Verwendung von Zuweisungen in diesem Kontext problematisch, und die Semantik war unklar. Die problematischen Konstruktoren wurden entfernt.

Korrekturen für not_fn()

P0358R1 Neue Formulierungen für std::not_fn bieten Unterstützung für die Weitergabe der Wertkategorie bei einem Wrapperaufruf.

shared_ptr<T[]>, shared_ptr<T[N]>

P0414R2 Zusammenführen von shared_ptr-Änderungen aus Library Fundamentals in C++17. [14]

Korrigieren von shared_ptr für Arrays

P0497R0 Korrekturen an der shared_ptr-Unterstützung für Arrays. [14]

Erklärung zu insert_return_type

P0508R0 Die assoziativen Container mit eindeutigen Schlüsseln und die ungeordneten Container mit eindeutigen Schlüsseln besitzen die Memberfunktion insert, die einen geschachtelten insert_return_type-Typ zurückgibt. Dieser Rückgabetyp ist nun als Spezialisierung eines Typs definiert, der für den Iterator und NodeType des Containers parametrisiert ist.

Inlinevariablen für die Standardbibliothek

Für P0607R0 werden verschiedene in der Standardbibliothek deklarierte allgemeine Variablen jetzt inline deklariert.

Veraltete Features aus Anhang D

Anhang D des C++-Standards enthält alle Features, die veraltet sind. Dazu gehören auch shared_ptr::unique(), <codecvt> und namespace std::tr1. Wenn die Compileroption /std:c++17 oder höher festgelegt wird, werden fast alle Standardbibliotheksfeatures in Anhang D als veraltet markiert. Weitere Informationen finden Sie unter Features der Standardbibliothek in Anhang D sind als veraltet markiert.

Der std::tr2::sys-Namespace in <experimental/filesystem> gibt bei Verwendung von /std:c++14 jetzt standardmäßig eine Warnung zu veralteten Features aus und wird bei Verwendung von /std:c++17 und höher standardmäßig entfernt.

Verbesserte Konformität in <iostream> durch Vermeidung einer nicht standardmäßigen Erweiterung (explizite Spezialisierungen in der Klasse).

Die Standardbibliothek verwendet jetzt intern Variablenvorlagen.

Die Standardbibliothek wurde aufgrund von C++17-Compileränderungen aktualisiert. Dabei wurde u. a. noexcept im Typsystem hinzugefügt. Außerdem wurden dynamische Ausnahmespezifikationen entfernt.

Teilweise Änderung der Reihenfolge

Der Compiler weist nun den folgenden Code ordnungsgemäß zurück und gibt die richtige Fehlermeldung aus:

template<typename... T>
int f(T* ...)
{
    return 1;
}

template<typename T>
int f(const T&)
{
    return 2;
}

int main()
{
    int i = 0;
    f(&i);    // C2668
}
t161.cpp
t161.cpp(16): error C2668: 'f': ambiguous call to overloaded function
t161.cpp(8): note: could be 'int f<int*>(const T &)'
        with
        [
            T=int*
        ]
t161.cpp(2): note: or       'int f<int>(int*)'
t161.cpp(16): note: while trying to match the argument list '(int*)'

Das Problem im Beispiel oben ist, dass es zwei Unterschiede in den Typen gibt (const im Vergleich zu non-const und pack im Vergleich zu non-pack). Um den Compilerfehler zu beseitigen, entfernen Sie einen der Unterschiede. Dies ermöglicht es dem Compiler, die Funktionen eindeutig zu sortieren.

template<typename... T>
int f(T* ...)
{
    return 1;
}

template<typename T>
int f(T&)
{
    return 2;
}

int main()
{
    int i = 0;
    f(&i);
}

Ausnahmehandler

Handler, die auf ein Array oder einen Funktionstyp verweisen, stimmen niemals mit einem Ausnahmeobjekt überein. Der Compiler hält sich nun ordnungsgemäß an diese Regel und gibt eine Warnung der Stufe 4 aus: C4843. Auch werden Handler von char* oder wchar_t* nicht mehr einem Zeichenfolgenliteral zugeordnet, wenn /Zc:strictStrings verwendet wird.

int main()
{
    try {
        throw "";
    }
    catch (int (&)[1]) {} // C4843 (This should always be dead code.)
    catch (void (&)()) {} // C4843 (This should always be dead code.)
    catch (char*) {} // This should not be a match under /Zc:strictStrings
}
warning C4843: 'int (&)[1]': An exception handler of reference to array or function type is unreachable, use 'int*' instead
warning C4843: 'void (__cdecl &)(void)': An exception handler of reference to array or function type is unreachable, use 'void (__cdecl*)(void)' instead

Mit dem folgenden Code wird der Fehler vermieden:

catch (int (*)[1]) {}

Namespace std::tr1 ist veraltet

Der std::tr1-Namespace, der nicht dem Standard entspricht, ist nun sowohl im C++14- als auch im C++17-Modus als veraltet markiert. In Visual Studio-2017 Version 15.5 löst der folgende Code C4996 aus:

#include <functional>
#include <iostream>
using namespace std;

int main() {
    std::tr1::function<int (int, int)> f = std::plus<int>(); //C4996
    cout << f(3, 5) << std::endl;
    f = std::multiplies<int>();
    cout << f(3, 5) << std::endl;
}
warning C4996: 'std::tr1': warning STL4002: The non-standard std::tr1 namespace and TR1-only machinery are deprecated and will be REMOVED. You can define _SILENCE_TR1_NAMESPACE_DEPRECATION_WARNING to acknowledge that you have received this warning.

Um den Fehler zu beheben, entfernen Sie den Verweis auf den tr1-Namespace:

#include <functional>
#include <iostream>
using namespace std;

int main() {
    std::function<int (int, int)> f = std::plus<int>();
    cout << f(3, 5) << std::endl;
    f = std::multiplies<int>();
    cout << f(3, 5) << std::endl;
}

Features der Standardbibliothek in Anhang D sind als veraltet markiert

Wenn die Compileroption im /std:c++17 -Modus oder höher festgelegt wird, werden fast alle Standardbibliotheksfeatures in Anhang D als veraltet markiert.

In Visual Studio-2017 Version 15.5 löst der folgende Code C4996 aus:

#include <iterator>

class MyIter : public std::iterator<std::random_access_iterator_tag, int> {
public:
    // ... other members ...
};

#include <type_traits>

static_assert(std::is_same<MyIter::pointer, int*>::value, "BOOM");
warning C4996: 'std::iterator<std::random_access_iterator_tag,int,ptrdiff_t,_Ty*,_Ty &>::pointer': warning STL4015: The std::iterator class template (used as a base class to provide typedefs) is deprecated in C++17. (The <iterator> header is NOT deprecated.) The C++ standard has never required user-defined iterators to derive from std::iterator. To fix this warning, stop deriving from std::iterator and start providing publicly accessible typedefs named iterator_category, value_type, difference_type, pointer, and reference. Note that value_type is required to be non-const, even for constant iterators. You can define _SILENCE_CXX17_ITERATOR_BASE_CLASS_DEPRECATION_WARNING or _SILENCE_ALL_CXX17_DEPRECATION_WARNINGS to acknowledge that you have received this warning.

Um den Fehler zu beheben, befolgen Sie die Anweisungen im Text der Warnung, wie im folgenden Code gezeigt:

#include <iterator>

class MyIter {
public:
    typedef std::random_access_iterator_tag iterator_category;
    typedef int value_type;
    typedef ptrdiff_t difference_type;
    typedef int* pointer;
    typedef int& reference;

    // ... other members ...
};

#include <type_traits>

static_assert(std::is_same<MyIter::pointer, int*>::value, "BOOM");

Unreferenzierte lokale Variablen

In Visual Studio 15.5, wird die Warnung C4189 in weiteren Fällen ausgelöst, wie im folgenden Code veranschaulicht:

void f() {
    char s[2] = {0}; // C4189. Either use the variable or remove it.
}
warning C4189: 's': local variable is initialized but not referenced

Um den Fehler zu beheben, entfernen Sie die nicht verwendete Variable.

Einzeilige Kommentare

In Visual Studio 2017 Version 15.5 werden die Warnungen C4001 und C4179 nicht mehr vom C-Compiler ausgegeben. Zuvor wurden sie nur bei Verwendung des Compilerschalters /Za ausgegeben. Die Warnungen werden nicht mehr benötigt, weil einzeilige Kommentare seit C99 Teil des C-Standards sind.

/* C only */
#pragma warning(disable:4001) // C4619
#pragma warning(disable:4179)
// single line comment
//* single line comment */
warning C4619: #pragma warning: there is no warning number '4001'

Wenn der Code nicht abwärtskompatibel sein muss, können Sie die Warnung vermeiden, indem Sie die C4001/C4179-Unterdrückung entfernen. Wenn der Code abwärts kompatibel sein muss, unterdrücken Sie nur C4619.

/* C only */

#pragma warning(disable:4619)
#pragma warning(disable:4001)
#pragma warning(disable:4179)

// single line comment
/* single line comment */

__declspec-Attribute mit extern "C"-Verknüpfung

In früheren Versionen von Visual Studio hat der Compiler __declspec(...)-Attribute ignoriert, wenn __declspec(...) vor der extern "C"-Verknüpfungsspezifikation angewendet wurde. Dieses Verhalten führte dazu, dass Code generiert wurde, den der Benutzer nicht beabsichtigt hatte (mit möglichen Auswirkungen auf die Laufzeit). Die Warnung C4768 wurde in Visual Studio Version 15.3 hinzugefügt, ist standardmäßig jedoch deaktiviert. In Visual Studio 2017 Version 15.5 ist die Warnung standardmäßig aktiviert.

__declspec(noinline) extern "C" HRESULT __stdcall // C4768
warning C4768: __declspec attributes before linkage specification are ignored

Um den Fehler zu beheben, platzieren Sie die Verknüpfungsspezifikation vor dem Attribut __declspec:

extern "C" __declspec(noinline) HRESULT __stdcall

Diese neue Warnung C4768 wird für einige Windows SDK-Header ausgegeben, die mit Visual Studio 2017 15.3 oder älter ausgeliefert wurden (Beispiel: Version 10.0.15063.0, auch bekannt als RS2 SDK). Spätere Versionen von Windows SDK-Headern (speziell ShlObj.h und ShlObj_core.h) wurden jedoch so korrigiert, dass sie diese Warnung nicht ausgeben. Wenn Sie sehen, dass diese Warnung von Windows SDK-Headern ausgegeben wird, können Sie diese Aktionen ausführen:

  1. Wechseln Sie zum neuesten Windows SDK, das mit Visual Studio 2017 Version 15.5 ausgeliefert wurde.

  2. Deaktivieren Sie die Warnung um den #include-Teil der Windows SDK-Headeranweisung:

   #pragma warning (push)
   #pragma warning(disable:4768)
   #include <shlobj.h>
   #pragma warning (pop)

extern constexpr-Bindung

In früheren Versionen von Visual Studio hat der Compiler immer eine interne constexpr-Variablenverknüpfung ausgegeben, selbst wenn die Variable als extern markiert wurde. In Visual Studio 2017 Version 15.5 ermöglicht ein neuer Compilerschalter ( /Zc:externConstexpr ) das richtige, standardkonforme Verhalten. Letztendlich wird dieses Verhalten die Standardeinstellung sein.

extern constexpr int x = 10;
error LNK2005: "int const x" already defined

Wenn eine Headerdatei eine Variable enthält, die als extern constexpr deklariert ist, muss sie als __declspec(selectany) markiert werden, damit ihre doppelten Deklarationen richtig kombiniert werden:

extern constexpr __declspec(selectany) int x = 10;

typeid kann nicht für einen unvollständigen Klassentyp verwendet werden

In früheren Versionen von Visual Studio ließ der Compiler fälschlicherweise den folgenden Code zu, was zu potenziell falschen Typinformationen führte. In Visual Studio 2017 Version 15.5 löst der Compiler ordnungsgemäß einen Fehler aus:

#include <typeinfo>

struct S;

void f() { typeid(S); } //C2027 in 15.5
error C2027: use of undefined type 'S'

std::is_convertible-Zieltyp

std::is_convertible erfordert, dass der Zieltyp ein gültiger Rückgabetyp ist. In früheren Versionen von Visual Studio ließ der Compiler fälschlicherweise abstrakte Typen zu, was zu falscher Überladungsauflösung und unbeabsichtigtem Laufzeitverhalten führen konnte. Der folgende Code löst jetzt ordnungsgemäß c2338 aus:

#include <type_traits>

struct B { virtual ~B() = 0; };
struct D : public B { virtual ~D(); };

static_assert(std::is_convertible<D, B>::value, "fail"); // C2338 in 15.5

Um den Fehler zu vermeiden, sollten Sie bei der Verwendung von is_convertible Zeigertypen vergleichen, da ein Vergleich ohne Zeigertyp möglicherweise zu einem Fehler führt, wenn ein Typ abstrakt ist:

#include <type_traits>

struct B { virtual ~B() = 0; };
struct D : public B { virtual ~D(); };

static_assert(std::is_convertible<D *, B *>::value, "fail");

Entfernen der dynamischen Ausnahmespezifikation und noexcept

In C++17 ist throw() ein Alias für noexcept, throw(<type list>) und throw(...) werden entfernt, und bestimmte Typen können noexcept enthalten. Diese Änderung kann zu Quellkompatibilitätsproblemen mit Code führen, der mit C++14 oder früher konform ist. Die Option /Zc:noexceptTypes- kann verwendet werden, um zur C++14-Version von noexcept zurückzukehren, während generell der C++17-Modus verwendet wird. Dies ermöglicht es Ihnen, Ihren Quellcode zu aktualisieren, um ihn an C++17 anzupassen, ohne dass Sie Ihren gesamten throw()-Code zur gleichen Zeit neu schreiben müssen.

Der Compiler diagnostiziert nun auch eine größere Anzahl nicht übereinstimmender Ausnahmespezifikationen in Deklarationen im C++17-Modus oder mit /permissive- mit der neuen Warnung C5043.

Der folgende Code generiert C5043 und C5040 in Visual Studio 2017 Version 15.5, wenn der Schalter /std:c++17 verwendet wird:

void f() throw(); // equivalent to void f() noexcept;
void f() {} // warning C5043
void g() throw(); // warning C5040

struct A {
    virtual void f() throw();
};

struct B : A {
    virtual void f() { } // error C2694
};

Sie können die Fehler entfernen, während Sie weiterhin /std:c++17 verwenden, indem Sie entweder die Option /Zc:noexceptTypes- zur Befehlszeile hinzufügen oder Ihren Code aktualisieren, um wie im folgenden Beispiel gezeigt noexcept zu verwenden:

void f() noexcept;
void f() noexcept { }
void g() noexcept(false);

struct A {
    virtual void f() noexcept;
};

struct B : A {
    virtual void f() noexcept { }
};

Inlinevariablen

Statische constexpr -Datenmember sind nun implizit inline . Ihre Deklaration innerhalb einer Klasse ist nun also ihre Definition. Die Verwendung einer Out-of-Line-Definition für einen statischen static constexpr -Datenmember ist redundant und nun veraltet. Wenn in Visual Studio 2017, Version 15.5, die Option /std:c++17 verwendet wird, generiert der folgende Code jetzt die Warnung „C5041“:

struct X {
    static constexpr int size = 3;
};
const int X::size; // C5041: 'size': out-of-line definition for constexpr static data member is not needed and is deprecated in C++17

extern "C" __declspec(...) Warnung C4768 jetzt standardmäßig aktiviert

Die Warnung wurde in Visual Studio Version 2017 Version 15.3 hinzugefügt, war aber standardmäßig deaktiviert. In Visual Studio 2017 Version 15.5 ist die Warnung standardmäßig aktiviert. Weitere Informationen finden Sie unter Neue Warnung zu __declspec-Attributen.

Standardfunktionen und __declspec(nothrow)

Der Compiler erlaubte es bisher, Standardfunktionen mit __declspec(nothrow) zu deklarieren, wenn die entsprechenden Basis-/Memberfunktionen Ausnahmen zuließen. Dieses Verhalten steht im Widerspruch zum C++-Standard und kann zur Laufzeit zu undefiniertem Verhalten führen. Der Standard verlangt, dass solche Funktionen als gelöscht definiert werden, wenn es eine Abweichung von der Ausnahmespezifikation gibt. Unter /std:c++17 löst der folgende Code „C2280“ aus:

struct A {
    A& operator=(const A& other) { // No exception specification; this function may throw.
        ...
    }
};

struct B : public A {
    __declspec(nothrow) B& operator=(const B& other) = default;
};

int main()
{
    B b1, b2;
    b2 = b1; // error C2280: attempting to reference a deleted function.
             // Function was implicitly deleted because the explicit exception
             // specification is incompatible with that of the implicit declaration.
}

Um diesen Code zu korrigieren, entfernen Sie entweder __declspec(nothrow) aus der Standardfunktion, oder entfernen Sie = default, und geben Sie eine Definition für die Funktion zusammen mit der erforderlichen Ausnahmebehandlung an:

struct A {
    A& operator=(const A& other) {
        // ...
    }
};

struct B : public A {
    B& operator=(const B& other) = default;
};

int main()
{
    B b1, b2;
    b2 = b1;
}

noexcept und Teilspezialisierungen

Mit noexcept im Typsystem können Teilspezialisierungen für den Abgleich bestimmter aufrufbarer Typen möglicherweise nicht kompiliert werden oder nicht die primäre Vorlage auswählen. Der Grund hierfür ist eine fehlende Teilspezialisierung für Zeiger auf noexcept-Funktionen.

In solchen Fällen müssen Sie möglicherweise weitere Teilspezialisierungen hinzufügen, um die noexcept -Funktionszeiger und die noexcept -Zeiger auf Memberfunktionen verarbeiten zu können. Diese Überladungen sind nur im /std:c++17 -Modus oder höher zulässig. Wenn die Abwärtskompatibilität mit C++14 aufrechterhalten werden muss und Sie Code schreiben, den andere nutzen, sollten Sie diese neuen Überladungen innerhalb von #ifdef-Anweisungen schützen. Wenn Sie in einem eigenständigen Modul arbeiten, können Sie anstelle von #ifdef-Wächtern einfach den Schalter /Zc:noexceptTypes- für die Kompilierung verwenden.

Der folgende Code wird bei Verwendung von /std:c++14 kompiliert, bei Verwendung von /std:c++17 tritt jedoch der Fehler „C2027“ auf:

template <typename T> struct A;

template <>
struct A<void(*)()>
{
    static const bool value = true;
};

template <typename T>
bool g(T t)
{
    return A<T>::value;
}

void f() noexcept {}

int main()
{
    return g(&f) ? 0 : 1; // C2027: use of undefined type 'A<T>'
}

Der folgende Code ist bei Verwendung von /std:c++17 erfolgreich, da der Compiler die neue Teilspezialisierung A<void (*)() noexcept> auswählt:

template <typename T> struct A;

template <>
struct A<void(*)()>
{
    static const bool value = true;
};

template <>
struct A<void(*)() noexcept>
{
    static const bool value = true;
};

template <typename T>
bool g(T t)
{
    return A<T>::value;
}

void f() noexcept {}

int main()
{
    return g(&f) ? 0 : 1; // OK
}

Verbesserungen der Konformität in 15.6

C++17-Bibliotheksgrundlagen V1

P0220R1 integriert die technische Spezifikation der Bibliotheksgrundlagen für C++17 in die Standardbibliothek. Enthält Updates zu <experimental/tuple>, <experimental/optional>, <experimental/functional>, <experimental/any>, <experimental/string_view>, <experimental/memory>, <experimental/memory_resource>, und <experimental/algorithm>.

C++17: Verbessern der Vorlagenargumentableitung für die Standardvorlagenbibliothek

P0739R0 Verschieben Sie adopt_lock_t für scoped_lock an den Anfang der Parameterliste, um die konsistente Verwendung von scoped_lock zu ermöglichen. Lassen Sie zu, dass der Konstruktor std::variant in mehreren Fällen Teil einer Überladungsauflösung ist, um die Kopierzuweisung zu ermöglichen.

Verbesserungen der Konformität in 15.7

C++17: Umformulierung der Konstruktorvererbung

P0136R1 gibt an, dass eine using-Deklaration, die einen Konstruktor benennt, jetzt die zugehörigen Basisklassenkonstruktoren für Initialisierungen der abgeleiteten Klasse sichtbar macht, anstatt die weiteren abgeleiteten Klassenkonstruktoren zu deklarieren. Diese Umformulierung ist eine Änderung gegenüber C++14. In Visual Studio 2017 Version 15.7 und höher kann Code, der im /std:c++17 -Modus und höher in C++14 gültig ist und die Konstruktorvererbung verwendet, ungültig sein oder über eine andere Semantik verfügen.

Im folgenden Beispiel ist das Verhalten von C++14 dargestellt:

struct A {
    template<typename T>
    A(T, typename T::type = 0);
    A(int);
};

struct B : A {
    using A::A;
    B(int n) = delete; // Error C2280
};

B b(42L); // Calls B<long>(long), which calls A(int)
          //  due to substitution failure in A<long>(long).

Das folgende Beispiel zeigt das Verhalten bei Verwendung von /std:c++17 in Visual Studio 15.7:

struct A {
    template<typename T>
    A(T, typename T::type = 0);
    A(int);
};

struct B : A {
    using A::A;
    B(int n)
    {
        //do something
    }
};

B b(42L); // now calls B(int)

Weitere Informationen finden Sie unter Konstruktoren.

C++17: Erweiterte Aggregatinitialisierung

P0017R1

Wenn der Konstruktor einer Basisklasse zwar nicht öffentlich, aber für eine abgeleitete Klasse verfügbar ist, können Sie im /std:c++17 -Modus und höher in Visual Studio Version 2017 Version 15.7 nicht länger leere Klammern zum Initialisieren eines Objekts des abgeleiteten Typs verwenden. Das folgende Beispiel veranschaulicht das konforme Verhalten für C++14:

struct Derived;
struct Base {
    friend struct Derived;
private:
    Base() {}
};

struct Derived : Base {};
Derived d1; // OK. No aggregate init involved.
Derived d2 {}; // OK in C++14: Calls Derived::Derived()
               // which can call Base ctor.

In C++17 gilt Derived nun als Aggregattyp. Das bedeutet, dass die Initialisierung von Base über den privaten Standardkonstruktor direkt als Teil der erweiterten Aggregatinitialisierungsregel erfolgt. Zuvor wurde der private Konstruktor Base über den Derived-Konstruktor aufgrund der Friend-Deklaration erfolgreich aufgerufen. Das folgende Beispiel zeigt das C++17-Verhalten im /std:c++17 -Modus in Visual Studio Version 15.7:

struct Derived;
struct Base {
    friend struct Derived;
private:
    Base() {}
};
struct Derived : Base {
    Derived() {} // add user-defined constructor
                 // to call with {} initialization
};
Derived d1; // OK. No aggregate init involved.
Derived d2 {}; // error C2248: 'Base::Base': cannot access
               // private member declared in class 'Base'

C++17: Deklarieren von Nichttyp-Vorlagenparameter mit „auto“

P0127R2

Im /std:c++17 -Modus kann der Compiler jetzt den Typ eines mit auto deklarierten Vorlagenarguments ohne Typ ableiten:

template <auto x> constexpr auto constant = x;

auto v1 = constant<5>;      // v1 == 5, decltype(v1) is int
auto v2 = constant<true>;   // v2 == true, decltype(v2) is bool
auto v3 = constant<'a'>;    // v3 == 'a', decltype(v3) is char

Ein Effekt dieses neuen Features ist, dass gültiger C++14-Code nicht mehr gültig sein oder über eine andere Semantik verfügen kann. Beispielsweise sind einige Überladungen, die zuvor ungültig waren, nun gültig. Das folgende Beispiel zeigt den C++14-Code, der kompiliert wird, weil der Aufruf an example(p) an example(void*); gebunden ist. In Visual Studio 2017 Version 15.7 stellt die example-Funktionsvorlage im /std:c++17 -Modus die beste Übereinstimmung dar.

template <int N> struct A;
template <typename T, T N> int example(A<N>*) = delete;

void example(void *);

void sample(A<0> *p)
{
    example(p); // OK in C++14
}

Das folgende Beispiel zeigt den C++17-Code im /std:c++17 -Modus in Visual Studio 15.7:

template <int N> struct A;
template <typename T, T N> int example(A<N>*);

void example(void *);

void sample(A<0> *p)
{
    example(p); // C2280: 'int example<int,0>(A<0>*)': attempting to reference a deleted function
}

C++17: Elementare Zeichenfolgenkonvertierungen (partiell)

P0067R5 Gebietsschemaunabhängige Funktionen auf niedriger Ebene für Wechsel zwischen ganzen Zahlen und Zeichenfolgen und zwischen Gleitkommazahlen und Zeichenfolgen.

C++20: Vermeiden der unnötigen decay-Eigenschaft (partiell)

P0777R1 Fügt Differenzierung zwischen dem Konzept von „decay“ und der einfachen Entfernung von Konstanten- oder Verweisqualifizierern hinzu. Die neue Typeigenschaft remove_reference_t ersetzt in einigen Kontexten decay_t. Die Unterstützung für remove_cvref_t ist in Visual Studio 2019 implementiert.

C++17: Parallele Algorithmen

P0024R2 Die technische Spezifikation zur Parallelität ist mit nur wenigen Änderungen im Standard eingeschlossen.

C++17: hypot(x, y, z)

P0030R1 Für die Typen float , double und long double werden drei neue Überladungen zu std::hypot hinzugefügt. Jeder dieser Typen verfügt über drei Eingabeparameter.

C++17: <filesystem>

P0218R1 Übernimmt die Dateisystem-TS in die Standardversion, mit ein paar Veränderungen in der Formulierung.

C++17: Mathematische spezielle Funktionen

P0226R1 Übernimmt die vorherige technische Spezifikation für die mathematischen speziellen Funktionen in den Standardheader <cmath>.

C++17: Herleitungsregelwerk für die Standardbibliothek

P0433R2 Updates auf STL, um von der Nutzung von C++17 von P0091R3 zu profitieren, was Unterstützung für die Klassenvorlagenargumentableitung hinzufügt.

C++17: Elementare Zeichenfolgenkonvertierungen

P0682R1 Verschieben Sie die elementaren Funktionen für die Zeichenfolgenkonvertierung von P0067R5 in einen neuen Header, <charconv>, und führen Sie andere Verbesserungen durch, einschließlich der Fehlerbehandlung zur Verwendung von std::errc anstelle von std::error_code.

C ++ 17: constexpr für char_traits (partiell)

P0426R1 Änderungen an den std::traits_type-Memberfunktionen length, compare und find, damit std::string_view in konstanten Ausdrücken verfügbar wird. (In der Visual Studio 2017-Version 15.6 nur für Clang/LLVM unterstützt. In Version 15.7 ist die Unterstützung für ClXX auch nahezu vollständig.)

C++17: Standardargument in der primären Klassenvorlage

Diese Verhaltensänderung ist eine Vorbedingung für P0091R3 - Template argument deduction for class templates (Vorlagenargumentableitung für Klassenvorlagen).

Zuvor hat der Compiler das Standardargument in der primären Klassenvorlage ignoriert:

template<typename T>
struct S {
    void f(int = 0);
};

template<typename T>
void S<T>::f(int = 0) {} // Re-definition necessary

Im /std:c++17 -Modus in Visual Studio 2017 Version 15.7 wird das Standardargument nicht ignoriert:

template<typename T>
struct S {
    void f(int = 0);
};

template<typename T>
void S<T>::f(int) {} // Default argument is used

Abhängige Namensauflösung

Diese Verhaltensänderung ist eine Vorbedingung für P0091R3 - Template argument deduction for class templates (Vorlagenargumentableitung für Klassenvorlagen).

Im folgenden Beispiel löst der Compiler in Visual Studio 15.6 und früher D::type zu B<T>::type in der primären Klassenvorlage auf.

template<typename T>
struct B {
    using type = T;
};

template<typename T>
struct D : B<T*> {
    using type = B<T*>::type;
};

Für Version 15.7 von Visual Studio 2017 im Modus /std:c++17 ist das typename -Schlüsselwort in der using -Anweisung von D erforderlich. Wenn typename nicht vorhanden ist, löst der Compiler die Warnung C4346 ('B<T*>::type': dependent name is not a type) und den Fehler C2061 (syntax error: identifier 'type') aus:

template<typename T>
struct B {
    using type = T;
};

template<typename T>
struct D : B<T*> {
    using type = typename B<T*>::type;
};

C++17 [[nodiscard]]-Attribute: Anstieg der Warnstufe

In Visual Studio 2017, Version 15.7, wurde im /std:c++17-Modus die Warnstufe von C4834 von W3 auf W1 heraufgesetzt. Sie können die Warnung mithilfe einer Umwandlung in void oder durch Übergabe von /wd:4834 an den Compiler deaktivieren.

[[nodiscard]] int f() { return 0; }

int main() {
    f(); // warning C4834: discarding return value
         // of function with 'nodiscard'
}

Initialisierungsliste für Basisklassen für variadic-Vorlagenkonstruktoren

In früheren Editionen von Visual Studio war eine Initialisierungsliste für Basisklassen für variadic-Vorlagenkonstruktoren, in der Vorlagenargumente fehlten, fälschlicherweise ohne Fehler zulässig. In Visual Studio 2017 Version 15.7 wird ein Compilerfehler ausgelöst.

Das folgende Codebeispiel in Version 15.7 von Visual Studio 2017 löst den Fehler C2614

template<typename T>
struct B {};

template<typename T>
struct D : B<T>
{

    template<typename ...C>
    D() : B() {} // C2614: D<int>: illegal member initialization: 'B' is not a base or member
};

D<int> d;

Ändern Sie den B()-Ausdruck in B<T>(), um den Fehler zu beheben.

constexpr-Aggregatinitialisierung

In früheren Versionen des C++-Compilers wurde die constexpr -Aggregatinitialisierung falsch behandelt. Der Compiler akzeptierte ungültigen Code, in dem die Aggregatinitialisierungsliste über zu viele Elemente verfügte, und erzeugte ungültigen Objektcode dafür. Ein Beispiel für einen solchen Code wird im Folgenden gezeigt:

#include <array>
struct X {
    unsigned short a;
    unsigned char b;
};

int main() {
    constexpr std::array<X, 2> xs = { // C2078: too many initializers
        { 1, 2 },
        { 3, 4 }
    };
    return 0;
}

In Visual Studio 2017, Version 15.7 Update 3 und höher, löst das obige Beispiel jetzt „C2078“ aus. Im unten stehenden Codebeispiel wird veranschaulicht, wie der Code behoben wird. Beim Initialisieren eines std::array mit geschachtelten „brace-init-list“-Objekten weisen Sie dem inneren Array selbst ein „braced-list“ zu:

#include <array>
struct X {
    unsigned short a;
    unsigned char b;
};

int main() {
    constexpr std::array<X, 2> xs = {{ // note double braces
        { 1, 2 },
        { 3, 4 }
    }}; // note double braces
    return 0;
}

Verbesserungen der Konformität in 15.8

typename für nicht qualifizierte Bezeichner

Im Modus /permissive- werden falsche typename -Schlüsselwörter für nicht qualifizierte Bezeichner in Definitionen von Aliasvorlagen vom Compiler nicht mehr akzeptiert. Der folgende Code löst jetzt „C7511“ aus:

template <typename T>
using  X = typename T; // C7511: 'T': 'typename' keyword must be 
                       // followed by a qualified name

Ändern Sie die zweite Zeile in using X = T;, um den Fehler zu beheben.

__declspec() auf der rechten Seite der Definitionen von Aliasvorlagen

__declspec ist auf der rechten Seite einer Aliasvorlagendefinition nicht mehr zulässig. Zuvor hat der Compiler diesen Code akzeptiert, jedoch ignoriert. Bei Verwendung des Alias wurde keine Warnung zu veralteten Features generiert.

Stattdessen kann das C++-Standardattribut [[deprecated]] verwendet werden, dieses wird ab Visual Studio 2017, Version 15.6, berücksichtigt. Der folgende Code löst jetzt „C2760“ aus:

template <typename T>
using X = __declspec(deprecated("msg")) T; // C2760: syntax error:
                                           // unexpected token '__declspec',
                                           // expected 'type specifier'`

Ersetzen Sie den Code mit dem Folgenden (mit den Attributen vor dem „=“ der Aliasdefinition), um den Fehler zu beheben:

template <typename T>
using  X [[deprecated("msg")]] = T;

Diagnose der Zweiphasennamenssuche

Die Zweiphasennamenssuche erfordert, dass nicht abhängige Namen in Vorlagentexten zum Zeitpunkt der Definition für die Vorlage sichtbar sind. Zuvor hätte der Microsoft C++-Compiler einen nicht gefundenen Namen bis zu den Instanziierungszeitpunkten nicht gesucht. Nun müssen nicht abhängige Namen im Vorlagentext gebunden sein.

Dies kann sich beispielsweise ergeben, wenn in abhängigen Basisklassen gesucht wird. Zuvor war im Compiler die Verwendung von Namen zulässig, die in abhängigen Basisklassen definiert sind. Der Grund dafür ist, dass diese Namen während der Instanziierung ermittelt wurden, wenn alle Typen aufgelöst werden. Nun wird dieser Code als Fehler behandelt. In diesen Fällen können Sie erzwingen, dass nach der Variable zum Instanziierungszeitpunkt gesucht wird, indem Sie sie mit dem Basisklassentyp qualifizieren oder anderweitig abhängig machen, indem Sie beispielsweise einen this->-Pointer hinzufügen.

Im Modus /permissive- löst der folgende Code jetzt „C3861“ aus:

template <class T>
struct Base {
    int base_value = 42;
};

template <class T>
struct S : Base<T> {
    int f() {
        return base_value; // C3861: 'base_value': identifier not found
    }
};

Ändern Sie die Anweisung return in return this->base_value;, um den Fehler zu beheben.

Hinweis

In den Boost.Python-Bibliotheksversionen vor 1.70 gab es eine MSVC-spezifische Problemumgehung für eine Vorlagenvorwärtsdeklaration in unwind_type.hpp. Im /permissive--Modus ab Visual Studio 2017, Version 15.8 (_MSC_VER==1915), führt der MSVC-Compiler die argumentabhängige Namenssuche ordnungsgemäß aus. Das Verhalten entspricht jetzt dem Verhalten anderer Compiler, sodass diese Problemumgehung nicht mehr erforderlich ist. Um den Fehler „C3861“ ('unwind_type': identifier not found) zu vermeiden, aktualisieren Sie Ihre Boost.Python-Bibliothek.

Vorwärtsdeklarationen und -definitionen im Namespace std

Standardmäßig ist es in C++ nicht zulässig, dem Namespace std Vorwärtsdeklarationen oder -definitionen hinzuzufügen. Das Hinzufügen von Deklarationen oder Definitionen in den Namespace std oder einen Namespace im Namespace std resultiert nun in nicht definiertem Verhalten.

Der Ort, an dem einige Standardbibliothekstypen definiert werden, soll zu einem zukünftigen Zeitpunkt verschoben werden. Durch diese Änderung wird vorhandener Code, der dem Namespace std Vorwärtsdeklarationen hinzufügt, unterbrochen. Eine neue Warnung (C4643) hilft beim Identifizieren solcher Probleme mit der Quelle. Im /default -Modus ist die Warnung aktiviert, standardmäßig ist sie deaktiviert. Dies wirkt sich auf Programme aus, die mit /Wall oder /WX kompiliert werden.

Der folgende Code löst nun den Fehler C4643 aus:

namespace std {
    template<typename T> class vector;  // C4643: Forward declaring 'vector'
                                        // in namespace std is not permitted
                                        // by the C++ Standard`
}

Um den Fehler zu beheben, verwenden Sie anstelle einer Vorwärtsdeklaration eine #include -Anweisung:

#include <vector>

Auf sich selbst verweisende Konstruktoren

Der C++-Standard schlägt vor, dass ein Compiler eine Diagnose ausgeben soll, wenn ein delegierender Konstruktor an sich selbst delegiert. Der Microsoft C++-Compiler in den Modi /std:c++17 und /std:c++latest löst jetzt „C7535“ aus.

Ohne diese Fehlermeldung wird das folgende Programm kompiliert, jedoch wird eine Endlosschleife erstellt:

class X {
public:
    X(int, int);
    X(int v) : X(v){} // C7535: 'X::X': delegating constructor calls itself
};

Delegieren Sie an einen anderen Konstruktor, um die Endlosschleife zu vermeiden:

class X {
public:

    X(int, int);
    X(int v) : X(v, 0) {}
};

offsetof mit konstanten Ausdrücken

offsetof wurde bisher üblicherweise mithilfe eines Makros implementiert, das reinterpret_cast erfordert. Die Verwendung ist in Kontexten nicht zulässig, die einen konstanten Ausdruck erfordern, jedoch war es bisher im Microsoft C++-Compiler gültig. Das offsetof-Makro, das mit der Standardbibliothek geliefert wird, verwendet korrekterweise eine intrinsische Compilerfunktion ( __builtin_offsetof ), jedoch haben viele Benutzer den Makrotrick angewendet, um offsetof selbst zu definieren.

In Visual Studio 2017 Version 15.8 beschränkt der Compiler die Bereiche, in denen diese reinterpret_cast-Operatoren im Standardmodus auftreten kann, damit der Code einfacher dem Standardverhalten von C++ entspricht. Im Modus /permissive- sind die Einschränkungen sogar strenger. Wenn ein offsetof-Ergebnis an Stellen verwendet wird, an denen konstante Ausdrücke erforderlich sind, löst der Code möglicherweise die Warnung „C4644“ oder „C2975“ aus.

Der folgende Code löst in Standard- und /std:c++17 -Modi C4644 und im /permissive--Modus C2975 aus:

struct Data {
    int x;
};

// Common pattern of user-defined offsetof
#define MY_OFFSET(T, m) (unsigned long long)(&(((T*)nullptr)->m))

int main()

{
    switch (0) {
    case MY_OFFSET(Data, x): return 0; // C4644: usage of the
        // macro-based offsetof pattern in constant expressions
        // is non-standard; use offsetof defined in the C++
        // standard library instead
        // OR
        // C2975: invalid template argument, expected
        // compile-time constant expression

    default: return 1;
    }
}

Verwenden Sie offsetof wie über <cstddef> definiert, um den Fehler zu beheben:

#include <cstddef>

struct Data {
    int x;
};

int main()
{
    switch (0) {
    case offsetof(Data, x): return 0;
    default: return 1;
    }
}

CV-Qualifizierer auf Basisklassen, die einer Paketerweiterung unterliegen

Vorherige Versionen des Microsoft C++-Compilers haben nicht erkannt, dass Basisklassen über CV-Qualifizierer verfügen, wenn sie ebenfalls Paketerweiterungen unterliegen.

In Visual Studio 2017, Version 15.8, löst der folgende Code im Modus /permissive- die Warnung „C3770“ aus:

template<typename... T>
class X : public T... { };

class S { };

int main()
{
    X<const S> x; // C3770: 'const S': is not a valid base class
}

template-Schlüsselwort und geschachtelte Namensspezifizierer

Im Modus /permissive- erfordert der Compiler nun das Schlüsselwort template , das einem Vorlagennamen vorangestellt wird, wenn dieser hinter einem abhängigen geschachtelten Namensspezifizierer steht.

Im Modus /permissive- löst der folgende Code jetzt C7510 aus:

template<typename T> struct Base
{
    template<class U> void example() {}
};

template<typename T>
struct X : Base<T>
{
    void example()
    {
        Base<T>::example<int>(); // C7510: 'example': use of dependent
            // template name must be prefixed with 'template'
            // note: see reference to class template instantiation
            // 'X<T>' being compiled
    }
};

Fügen Sie der Anweisung Base<T>::example<int>(); das Schlüsselwort template wie im folgenden Beispiel gezeigt hinzu, um den Fehler zu beheben:

template<typename T> struct Base
{
    template<class U> void example() {}
};

template<typename T>
struct X : Base<T>
{
    void example()
    {
        // Add template keyword here:
        Base<T>::template example<int>();
    }
};

Verbesserungen der Konformität in 15.9

Auswertungsreihenfolge von links nach rechts für die Operatoren ->*, [], >> und <<

Ab C++17 müssen die Operanden der Operatoren ->*, [], >> und << in der Reihenfolge von links nach rechts ausgewertet werden. Es gibt zwei Fälle, in denen der Compiler diese Reihenfolge nicht sicherstellen kann:

  • wenn es sich bei einem der Operandenausdrücke um ein nach Wert übergebenes Objekt handelt oder er ein nach Wert übergebenes Objekt enthält oder

  • wenn er mithilfe von /clr kompiliert wird und einer der Operanden ein Feld eines Objekts oder ein Arrayelement ist.

Der Compiler gibt die Warnung C4866 aus, wenn er die Auswertung von links nach rechts nicht sicherstellen kann. Diese Warnung wird vom Compiler nur generiert, wenn /std:c++17 oder höher angegeben ist, da die Anforderung der Auswertungsreihenfolge von links nach rechts für diese Operatoren in C++17 eingeführt wurde.

Um diese Warnung zu vermeiden, sollten Sie zunächst überprüfen, ob die Auswertung von links nach rechts für die Operanden erforderlich ist. Dies kann beispielsweise erforderlich sein, wenn durch die Auswertung der Operanden reihenfolgenabhängige Nebenwirkungen auftreten. In vielen Fällen hat die Reihenfolge, in der Operanden ausgewertet werden, keinen beobachtbaren Effekt. Wenn die Auswertungsreihenfolge von links nach rechts sein muss, überlegen Sie, ob Sie die Operanden stattdessen als const-Verweis übergeben können. Diese Änderung entfernt die Warnung im folgenden Codebeispiel:

// C4866.cpp
// compile with: /w14866 /std:c++17

class HasCopyConstructor
{
public:
    int x;

    HasCopyConstructor(int x) : x(x) {}
    HasCopyConstructor(const HasCopyConstructor& h) : x(h.x) { }
};

int operator>>(HasCopyConstructor a, HasCopyConstructor b) { return a.x >> b.x; }

// This version of operator>> does not trigger the warning:
// int operator>>(const HasCopyConstructor& a, const HasCopyConstructor& b) { return a.x >> b.x; }

int main()
{
    HasCopyConstructor a{ 1 };
    HasCopyConstructor b{ 2 };

    a>>b;        // C4866 for call to operator>>
};

Bezeichner in Memberaliasvorlagen

Ein Bezeichner, der in einer Memberalias-Vorlagendefinition verwendet wird, muss vor der Verwendung deklariert werden.

In vorherigen Compilerversionen war der folgende Code zulässig. In Visual Studio 2017 Version 15.9 löst der Compiler im /permissive--Modus C3861 aus:

template <typename... Ts>
struct A
{
  public:
    template <typename U>
    using from_template_t = decltype(from_template(A<U>{})); // C3861:
        // 'from_template': identifier not found

  private:
    template <template <typename...> typename Type, typename... Args>
    static constexpr A<Args...> from_template(A<Type<Args...>>);
};

A<>::from_template_t<A<int>> a;

Um den Fehler zu beheben, deklarieren Sie from_template_t vor from_template.

Moduländerungen

In Visual Studio 2017 Version 15.9 löst der Compiler immer dann den Fehler C5050 aus, wenn die Befehlszeilenoptionen für Module zwischen Modulerstellung und Modulverwendung nicht konsistent sind. Im folgenden Beispiel liegen zwei Probleme vor:

  • Auf der Verbraucherseite (main.cpp) ist die Option /EHsc nicht angegeben.

  • Die C++-Version lautet auf der Erstellerseite /std:c++17 und auf der Verbraucherseite /std:c++14 .

cl /EHsc /std:c++17 m.ixx /experimental:module
cl /experimental:module /module:reference m.ifc main.cpp /std:c++14

Der Compiler löst in beiden Fällen „C5050“ aus:

warning C5050: Possible incompatible environment while
importing module 'm': mismatched C++ versions.
Current "201402" module version "201703".

Darüber hinaus löst der Compiler immer dann C7536 aus, wenn die .ifc-Datei geändert wurde. Der Header der Modulschnittstelle enthält einen SHA2-Hash der darunter befindlichen Inhalte. Beim Import wird die .ifc -Datei mit einem Hash versehen und dann mit dem im Header angegebenen Hash verglichen. Wenn diese nicht übereinstimmen, wird der Fehler „C7536“ ausgelöst:

error C7536: ifc failed integrity checks.
Expected SHA2: '66d5c8154df0c71d4cab7665bab4a125c7ce5cb9a401a4d8b461b706ddd771c6'

Teilsortierung im Zusammenhang mit Aliasen und nicht abgeleiteten Kontexten

Es besteht ein Implementierungsunterschied bei den Regeln für die Teilsortierung im Zusammenhang mit Aliasen in nicht abgeleiteten Kontexten. Im folgenden Beispiel lösen GCC und der Microsoft C++-Compiler (im Modus /permissive-) einen Fehler aus, während Clang den Code akzeptiert.

#include <utility>
using size_t = std::size_t;

template <typename T>
struct A {};
template <size_t, size_t>
struct AlignedBuffer {};
template <size_t len>
using AlignedStorage = AlignedBuffer<len, 4>;

template <class T, class Alloc>
int f(Alloc &alloc, const AlignedStorage<T::size> &buffer)
{
    return 1;
}

template <class T, class Alloc>
int f(A<Alloc> &alloc, const AlignedStorage<T::size> &buffer)
{
    return 2;
}

struct Alloc
{
    static constexpr size_t size = 10;
};

int main()
{
    A<void> a;
    AlignedStorage<Alloc::size> buf;
    if (f<Alloc>(a, buf) != 2)
    {
        return 1;
    }

    return 0;
}

Das vorherige Beispiel führt zu C2668:

partial_alias.cpp(32): error C2668: 'f': ambiguous call to overloaded function
partial_alias.cpp(18): note: could be 'int f<Alloc,void>(A<void> &,const AlignedBuffer<10,4> &)'
partial_alias.cpp(12): note: or       'int f<Alloc,A<void>>(Alloc &,const AlignedBuffer<10,4> &)'
        with
        [
            Alloc=A<void>
        ]
partial_alias.cpp(32): note: while trying to match the argument list '(A<void>, AlignedBuffer<10,4>)'

Der Grund für den Implementierungsunterschied ist eine Regression in der C++-Standardformulierung. Bei der Lösung des Kernproblems 2235 wurde Text entfernt, der erlauben würde, dass diese Überladungen sortiert werden können. Der aktuelle C++-Standard bietet keinen Mechanismus für eine Teilsortierung dieser Funktionen, deshalb können sie als nicht eindeutig betrachtet werden.

Als Problemumgehung wird empfohlen, sich nicht auf die Teilsortierung zum Lösen dieses Problems zu verlassen. Verwenden Sie stattdessen SFINAE zum Entfernen von bestimmter Überladungen. Im folgenden Beispiel wird eine Hilfsklasse IsA verwendet, um die erste Überladung zu entfernen, wenn Alloc eine Spezialisierung von A ist:

#include <utility>
using size_t = std::size_t;

template <typename T>
struct A {};
template <size_t, size_t>
struct AlignedBuffer {};
template <size_t len>
using AlignedStorage = AlignedBuffer<len, 4>;

template <typename T> struct IsA : std::false_type {};
template <typename T> struct IsA<A<T>> : std::true_type {};

template <class T, class Alloc, typename = std::enable_if_t<!IsA<Alloc>::value>>
int f(Alloc &alloc, const AlignedStorage<T::size> &buffer)
{
    return 1;
}

template <class T, class Alloc>
int f(A<Alloc> &alloc, const AlignedStorage<T::size> &buffer)
{
    return 2;
}

struct Alloc
{
    static constexpr size_t size = 10;
};

int main()
{
    A<void> a;
    AlignedStorage<Alloc::size> buf;
    if (f<Alloc>(a, buf) != 2)
    {
        return 1;
    }

    return 0;
}

Ungültige Ausdrücke und Nichtliteraltypen in Definitionen von Vorlagenfunktionen

Ungültige Ausdrücke und Nichtliteraltypen werden nun richtig in Definitionen von Vorlagenfunktionen diagnostiziert, die explizit spezialisiert werden. Bisher wurden solche Fehler nicht für die Funktionsdefinition ausgegeben. Der ungültige Ausdruck oder Nichtliteraltyp wurde aber trotzdem diagnostiziert, wenn er als Teil eines konstanten Ausdrucks ausgewertet wurde.

In früheren Versionen von Visual Studio wird der folgende Code ohne Warnung kompiliert:

void g();

template<typename T>
struct S
{
    constexpr void f();
};

template<>
constexpr void S<int>::f()
{
    g(); // C3615 in 15.9
}

In Visual Studio 2017 Version 15.9 führt der Code zu Fehler C3615:

error C3615: constexpr function 'S<int>::f' cannot result in a constant expression.
note: failure was caused by call of undefined function or one not declared 'constexpr'
note: see usage of 'g'.

Entfernen Sie den Qualifizierer constexpr aus der expliziten Instanziierungsanweisung der Funktion f(), um diesen Fehler zu vermeiden.

Siehe auch

Microsoft C/C++-Sprachkonformität