Share via


Verbeteringen in C++-conformiteit, gedragswijzigingen en bugfixes in Visual Studio 2017

Microsoft C/C++ in Visual Studio (MSVC) maakt nalevingsverbeteringen en bugfixes in elke release. Dit artikel bevat de verbeteringen per primaire release en vervolgens per versie. Als u rechtstreeks naar de wijzigingen voor een specifieke versie wilt gaan, gebruikt u de onderstaande lijst in dit artikel.

Dit document bevat de wijzigingen in Visual Studio 2017. Zie C++ conformanceverbeteringen in Visual Studio 2022 voor een gids van de wijzigingen in Visual Studio 2022. Voor een handleiding over de wijzigingen in Visual Studio 2019, zie C++-conformiteitsverbeteringen in Visual Studio 2019. Zie Visual C++ Wat is er nieuw 2003 tot en met 2015 voor een volledige lijst met verbeteringen in de conformiteit.

Conformiteitsverbeteringen in Visual Studio 2017 RTW (versie 15.0)

Met ondersteuning voor gegeneraliseerde constexpr en niet-statische initialisatie van gegevensleden (NSDMI) voor aggregaties is de MSVC-compiler in Visual Studio 2017 nu voltooid voor functies die zijn toegevoegd aan de C++14-standaard. De compiler mist echter nog steeds enkele functies van de C++11- en C++98-standaarden. Zie de taalconformance van Microsoft C/C++ voor de huidige status van de compiler.

C++11: SFINAE-ondersteuning voor expressies in meer bibliotheken

De compiler blijft de ondersteuning voor expression-SFINAE verbeteren. Dit is vereist voor de aftrek en vervanging van sjabloonargumenten, waarbij decltype en constexpr expressies kunnen worden weergegeven als sjabloonparameters. Zie Expression SFINAE-verbeteringen in Visual Studio 2017 RC voor meer informatie.

C++14: NSDMI voor aggregaties

Een aggregaties zijn een matrix of een klasse met: geen door de gebruiker geleverde constructor, geen niet-statische gegevensleden die privé of beveiligd zijn, geen basisklassen en geen virtuele functies. Vanaf C++14 kunnen aggregaties lid-initializers bevatten. Zie Initializers en aggregaties voor leden voor meer informatie.

C++14: Uitgebreid constexpr

Expressies die zijn gedeclareerd als constexpr, mogen nu bepaalde soorten declaraties bevatten, if- en switch-instructies, lusinstructies en mutatie van objecten waarvan de levensduur binnen de constexpr expressie-evaluatie is begonnen. Er is geen vereiste meer dat een constexpr niet-statische lidfunctie impliciet constmoet zijn. Voor meer informatie, zie Ontspannen beperkingen voor constexpr functies.

C++17: Terse static_assert

de berichtparameter static_assert is optioneel. Zie N3928: Uitbreiden static_assert, v2 voor meer informatie.

C++17: [[fallthrough]] kenmerk

In de /std:c++17-modus en later kan het [[fallthrough]]-attribuut worden gebruikt in de context van switch-verklaringen als een hint aan de compiler dat het fall-through-gedrag wordt bedoeld. Met dit kenmerk voorkomt u dat de compiler in dergelijke gevallen waarschuwingen uitgeeft. Zie P0188R0 - Wording for [[fallthrough]] attribute voor meer informatie.

Gegeneraliseerde bereik-gebaseerde for lussen

Op bereik gebaseerde for lussen vereisen niet langer dat begin() en end() objecten van hetzelfde type retourneren. Deze wijziging maakt het end() mogelijk om een sentinel te retourneren zoals wordt gebruikt door reeksen in range-v3 en de voltooide, maar nog niet gepubliceerde technische specificatie voor reeksen. Zie P0184R0 - Generalizing the Range-Based for Loop voor meer informatie.

Copy-lijst-initialisatie

Visual Studio 2017 genereert correct compilerfouten met betrekking tot het maken van objecten met behulp van initialisatielijsten. Deze fouten zijn niet opgetreden in Visual Studio 2015 en kunnen leiden tot crashes of niet-gedefinieerd runtimegedrag. Op basis van N4594 13.3.1.7p1, in copy-list-initialization, moet de compiler een expliciete constructor in overweging nemen voor de overbelastingsresolutie. Er moet echter een fout optreden als die specifieke overbelasting wordt gekozen.

De volgende twee voorbeelden worden gecompileerd in Visual Studio 2015, maar niet 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 &'

}

Gebruik directe initialisatie om de fout te corrigeren:

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

In Visual Studio 2015 behandelde de compiler foutieve copy-list-initialisatie op dezelfde manier als reguliere copy-initialisatie: het beschouwd als alleen het converteren van constructors voor overbelastingsresolutie. In het volgende voorbeeld kiest MyInt(23)Visual Studio 2015. Visual Studio 2017 genereert de fout correct.

// From https://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
}

Dit voorbeeld is vergelijkbaar met de vorige, maar veroorzaakt een andere fout. Het lukt in Visual Studio 2015 en mislukt in Visual Studio 2017 met 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
}

Afgeschafte typedefs

Visual Studio 2017 geeft nu de juiste waarschuwing uit voor afgeschafte typedefs die zijn gedeclareerd in een klasse of struct. Het volgende voorbeeld wordt gecompileerd zonder waarschuwingen in Visual Studio 2015. Het produceert C4996 in Visual Studio 2017.

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 genereert een fout wanneer de linkeroperand van een voorwaardelijk evaluerende bewerking niet geldig is in een constexpr context. De volgende code compileert in Visual Studio 2015, maar niet in Visual Studio 2017, waar het de foutmelding C3615 genereert.

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
}

Als u de fout wilt corrigeren, declareert u de array::size() functie als constexpr of verwijdert u de constexpr kwalificatie van f.

Klassetypen die aan variadische functies worden doorgegeven

In Visual Studio 2017 moeten klassen of structs die aan een variadische functie, zoals printf, worden doorgegeven, trivially copyable zijn. Wanneer dergelijke objecten worden doorgegeven, maakt de compiler gewoon een bitsgewijze kopie en roept de constructor of destructor niet aan.

#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
}

U kunt de fout corrigeren door een lidfunctie aan te roepen die een trivially copyable type retourneert.

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

of gebruik een statische cast om het object te converteren voordat u het doorgeeft:

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

Voor tekenreeksen die zijn gebouwd en beheerd met behulp CStringvan, moet de opgegeven operator LPCTSTR() worden gebruikt om een CString object naar de C-aanwijzer te casten die wordt verwacht door de notatietekenreeks.

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

Cv-kwalificatoren in klasseconstructie

In Visual Studio 2015 negeert de compiler soms ten onrechte de cv-kwalificatie bij het genereren van een klasseobject via een constructor-aanroep. Dit probleem kan leiden tot een crash of onverwacht runtimegedrag. In het volgende voorbeeld wordt gecompileerd in Visual Studio 2015, maar wordt een compilerfout gegenereerd in Visual Studio 2017:

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

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

Als u de fout wilt corrigeren, declareert u operator int() als const.

Toegang controleren op gekwalificeerde namen in sjablonen

In eerdere versies van de compiler is de toegang tot gekwalificeerde namen in sommige sjablooncontexten niet gecontroleerd. Dit probleem kan het verwachte SFINAE-gedrag verstoren, waarbij de vervanging naar verwachting mislukt vanwege de ontoegankelijkheid van een naam. Het kan mogelijk een crash of onverwacht gedrag hebben veroorzaakt tijdens runtime, omdat de compiler ten onrechte de verkeerde overbelasting van de operator heeft genoemd. In Visual Studio 2017 treedt een compilerfout op. De specifieke fout kan variëren, maar een typische fout is C2672, 'geen overeenkomende overbelaste functie gevonden'. De volgende code compileert in Visual Studio 2015, maar genereert een fout in Visual Studio 2017:

#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.
}

Ontbrekende sjabloonargumentlijsten

In Visual Studio 2015 en eerder heeft de compiler niet alle ontbrekende sjabloonargumentlijsten vastgesteld. Het zou niet opmerken wanneer de ontbrekende sjabloon verscheen in een sjabloonparameterlijst: bijvoorbeeld wanneer een deel van een standaardsjabloonargument of een niet-type sjabloonparameter ontbrak. Dit probleem kan leiden tot onvoorspelbaar gedrag, waaronder compiler crashes of onverwacht runtime gedrag. De volgende code wordt gecompileerd in Visual Studio 2015, maar produceert een fout in Visual Studio 2017.

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;

Expression-SFINAE

Ter ondersteuning van expression-SFINAE parseert de compiler nu decltype argumenten wanneer sjablonen worden gedeclareerd in plaats van geïnstantieerd. Dus als er een niet-afhankelijke specialisatie in het decltype argument wordt gevonden, wordt deze niet uitgesteld tot de instantiëringstijd. Het wordt onmiddellijk verwerkt en eventuele resulterende fouten worden op dat moment aangegeven.

In het volgende voorbeeld ziet u een dergelijke compilerfout die wordt gegenereerd op het moment van declaratie:

#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");

Klassen gedeclareerd in anonieme naamruimten

Volgens de C++-standaard heeft een klasse die is gedeclareerd in een anonieme naamruimte interne koppeling en dat betekent dat deze niet kan worden geëxporteerd. In Visual Studio 2015 en eerder is deze regel niet afgedwongen. In Visual Studio 2017 wordt de regel gedeeltelijk afgedwongen. In Visual Studio 2017 wordt in het volgende voorbeeld fout C2201 gegenereerd:

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

Standaard initializers voor waardeklasseleden (C++/CLI)

In Visual Studio 2015 en eerder heeft de compiler een standaard initialisatiefunctie voor leden toegestaan (maar genegeerd) voor een lid van een waardeklasse. Bij standaard initialisatie van een waardeklasse worden de leden altijd met nul geïnitialiseerd. Een standaardconstructor is niet toegestaan. In Visual Studio 2017 veroorzaken standaard initialisatieprogramma's voor leden een compilerfout, zoals wordt weergegeven in dit voorbeeld:

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

Standaardindexeerfuncties (C++/CLI)

In Visual Studio 2015 en eerder heeft de compiler in sommige gevallen een standaardeigenschap verkeerd geïdentificeerd als een standaardindexeerfunctie. Het was mogelijk om het probleem te omzeilen door de id default te gebruiken om toegang te krijgen tot de eigenschap. De tijdelijke oplossing zelf werd problematisch nadat default als trefwoord werd geïntroduceerd in C++11. In Visual Studio 2017 zijn de fouten die de tijdelijke oplossing vereist, opgelost. De compiler genereert nu een fout wanneer default deze wordt gebruikt voor toegang tot de standaardeigenschap voor een klasse.

//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 hebt u toegang tot beide waarde-eigenschappen op naam:

#using "class1.dll"

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

Conformiteitsverbeteringen in 15.3

constexpr lambdas

Lambda-expressies kunnen nu worden gebruikt in constante expressies. Zie constexpr lambda-expressies in C++voor meer informatie.

if constexpr in sjablonen voor functies

Een functiesjabloon kan instructies bevatten if constexpr voor het inschakelen van compileertijdvertakkingen. Zie if constexpr verklaringen voor meer informatie.

Selectieverklaringen met initialisators

Een if instructie kan een initialisatiefunctie bevatten die een variabele introduceert op blokbereik binnen de instructie zelf. Zie if instructies met initialisatiefunctie voor meer informatie.

[[maybe_unused]] en [[nodiscard]] kenmerken

Nieuwe kenmerk [[maybe_unused]] geeft waarschuwingen stil wanneer een entiteit niet wordt gebruikt. Het [[nodiscard]] kenmerk maakt een waarschuwing als de retourwaarde van een functie-aanroep wordt verwijderd. Zie Kenmerken in C++voor meer informatie.

Kenmerknaamruimten zonder herhaling gebruiken

Nieuwe syntaxis om slechts één naamruimte-id in te schakelen in een kenmerklijst. Zie Kenmerken in C++voor meer informatie.

Gestructureerde bindingen

Het is nu mogelijk in één declaratie om een waarde op te slaan met afzonderlijke namen voor de onderdelen, wanneer de waarde een matrix, een std::tuple of std::pair, of alle openbare niet-statische gegevensleden bevat. Zie P0144R0 - Structured Bindings en Terugkeer van meerdere waarden uit een functie voor meer informatie.

Bouwregels voor enum class waarden

Er is nu een impliciete conversie voor begrensde enumeraties die niet-verengend is. Het converteert van het onderliggende type van een gescopeerde enumeratie naar de enumeratie zelf. De conversie is beschikbaar wanneer de definitie geen enumerator introduceert en wanneer de bron een syntaxis voor lijst-initialisatie gebruikt. Zie en P0138R2 - Construction Rules for enum class Values voor meer informatie.

Vastleggen op *this waarde

Het *this object in een lambda-expressie kan nu worden vastgelegd op waarde. Deze wijziging maakt scenario's mogelijk waarin de lambda parallel en asynchrone bewerkingen wordt aangeroepen, met name op nieuwere machinearchitecturen. Zie P0018R3 - Lambda Capture of *this by Value as [=,*this] voor meer informatie.

Verwijderen operator++ voor bool

operator++ wordt niet meer ondersteund voor bool typen. Zie P0002R1 - Remove Deprecated operator++(bool) voor meer informatie.

Verouderd register trefwoord verwijderen

Het register trefwoord, dat eerder is afgeschaft (en genegeerd door de compiler), wordt nu uit de taal verwijderd. Zie P0001R1 - Remove Deprecated Use of the register Keyword voor meer informatie.

Aanroepen naar verwijderde lidsjablonen

In eerdere versies van Visual Studio faalde de compiler er in sommige gevallen in om een fout te geven voor onjuist gevormde aanroepen naar een verwijderde lid-sjabloon. Deze aanroepen veroorzaken mogelijk crashes tijdens runtime. De volgende code produceert nu 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
}

Als u de fout wilt oplossen, declareert u i als int.

Controles vooraf op typeeigenschappen

Visual Studio 2017 versie 15.3 verbetert voorwaardelijke controles op type-kenmerken om strikter de standaard te volgen. Een dergelijke controle is gericht op de toewijsbaarheid. De volgende code produceert C2139 in Visual Studio 2017 versie 15.3:

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

Nieuwe compilerwaarschuwing en runtime-controles op systeemeigen naar beheerde marshaling

Voor het aanroepen van beheerde functies naar systeemeigen functies is marshaling vereist. De CLR doet de marshaling, maar het begrijpt geen C++-semantiek. Als u een systeemeigen object op waarde doorgeeft, roept CLR de copy-constructor van het object aan of gebruikt BitBlt, wat kan leiden tot niet-gedefinieerd gedrag tijdens runtime.

De compiler verzendt nu een waarschuwing als deze fout op het moment van compileren wordt gevonden: een systeemeigen object met verwijderde kopieerctor wordt doorgegeven tussen een systeemeigen en beheerde grens op waarde. Voor gevallen waarin de compiler op compileertijd niet weet, voegt het een runtimecontrole toe, zodat het programma std::terminate onmiddellijk aanroept wanneer er een ongeldige marshaling plaatsvindt. In Visual Studio 2017 versie 15.3 produceert de volgende code waarschuwing 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.
}

Als u de fout wilt oplossen, verwijdert u de #pragma managed directive om de beller als native te markeren en marshaling te voorkomen.

Experimentele API-waarschuwing voor WinRT

WinRT-API's die worden uitgebracht voor experimenten en feedback, zijn voorzien van Windows.Foundation.Metadata.ExperimentalAttribute. In Visual Studio 2017 versie 15.3 produceert de compiler waarschuwing C4698 voor dit kenmerk. Enkele API's in eerdere versies van de Windows SDK zijn al ingericht met het kenmerk en aanroepen naar deze API's activeren nu deze compilerwaarschuwing. Nieuwere Windows SDK's hebben het kenmerk verwijderd uit alle verzonden typen. Als u een oudere SDK gebruikt, moet u deze waarschuwingen onderdrukken bij alle aanroepen van uitgeleverde typen.

De volgende code produceert waarschuwing C4698:

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

Als u de waarschuwing wilt uitschakelen, voegt u een #pragma toe:

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

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

#pragma warning(pop)

Extern gedefinieerde lidfunctie voor een sjabloon

Visual Studio 2017 versie 15.3 produceert een fout voor een out-of-line definitie van een sjabloonlidfunctie die niet in de klasse is gedeclareerd. De volgende code produceert nu fout C2039:

struct S {};

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

Als u de fout wilt oplossen, voegt u een declaratie toe aan de klasse:

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

Poging om het adres van de aanwijzer this te nemen

In C++ is this een prvalue van het type aanwijzer naar X. Je kunt het adres van this niet nemen of het aan een lvalue-verwijzing koppelen. In eerdere versies van Visual Studio zou de compiler u in staat stellen om deze beperking te omzeilen door gebruik te maken van een cast. In Visual Studio 2017 versie 15.3 produceert de compiler fout C2664.

Conversie naar een niet-toegankelijke basisklasse

Visual Studio 2017 versie 15.3 produceert een fout wanneer u probeert een type te converteren naar een basisklasse die niet toegankelijk is. De volgende code is ongeldig gevormd en kan tijdens runtime een crash veroorzaken. De compiler produceert nu C2243 wanneer deze code als volgt ziet:

#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());
}

Standaardargumenten zijn niet toegestaan voor niet-regeldefinities van lidfuncties

Standaardargumenten zijn niet toegestaan voor out-of-line definities van lidfuncties in sjabloonklassen. De compiler geeft een waarschuwing onder /permissiveen een harde fout onder /permissive-.

In eerdere versies van Visual Studio kan de volgende slecht gevormde code mogelijk een runtime-crash veroorzaken. Visual Studio 2017 versie 15.3 produceert waarschuwing C5037:

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
{
    // ...
}

Als u de fout wilt oplossen, verwijdert u het = false standaardargument.

Gebruik van offsetof met samengesteld lidaanduiding

In Visual Studio 2017 versie 15.3, waarbij offsetof(T, m)m een 'samengesteld lid designator' is, resulteert dit in een waarschuwing wanneer u compileert met de /Wall optie. De volgende code is ongeldig gevormd en kan tijdens runtime een crash veroorzaken. Visual Studio 2017 versie 15.3 produceert waarschuwing 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]);

Als u de code wilt herstellen, schakelt u de waarschuwing uit met een pragma of wijzigt u de code zodat deze niet wordt gebruikt offsetof:

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

Gebruiken offsetof met statische gegevenslid- of lidfunctie

In Visual Studio 2017 versie 15.3, waarbij offsetof(T, m)m verwijst naar een statisch gegevenslid of een lidfunctie, resulteert dit in een fout. De volgende code produceert fout 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'

Deze code is slecht gevormd en kan tijdens runtime een crash veroorzaken. Als u de fout wilt oplossen, wijzigt u de code om niet-gedefinieerd gedrag niet meer aan te roepen. Het is niet-draagbare code die niet is toegestaan volgens de C++-standaard.

Nieuwe waarschuwing voor __declspec kenmerken

In Visual Studio 2017 versie 15.3 negeert de compiler geen kenmerken meer wanneer __declspec(...) vóór de koppelingsspecificatie extern "C" wordt toegepast. Voorheen negeerde de compiler het kenmerk, wat runtime-gevolgen zou kunnen hebben. Wanneer de /Wall en /WX opties zijn ingesteld, produceert de volgende code waarschuwing C4768:

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

Als u de waarschuwing wilt herstellen, plaatst u extern "C" eerst:

extern "C" __declspec(noinline) HRESULT __stdcall

Deze waarschuwing is standaard uitgeschakeld in Visual Studio 2017 versie 15.3 en heeft alleen invloed op code die is gecompileerd met /Wall/WX. Vanaf Visual Studio 2017 versie 15.5 is deze standaard ingeschakeld als waarschuwing op niveau 3.

decltype en aanroepen naar verwijderde destructors

In eerdere versies van Visual Studio heeft de compiler niet gedetecteerd wanneer er een aanroep naar een verwijderde destructor is opgetreden in de context van de expressie die is gekoppeld aan decltype. In Visual Studio 2017 versie 15.3 produceert de volgende code fout 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
}

Niet-geïnitialiseerde constvariabelen

Visual Studio 2017 RTW-release had een regressie: de C++-compiler zou geen diagnose geven voor een niet-geïnitialiseerde const variabele. Deze regressie is opgelost in Visual Studio 2017 versie 15.3. De volgende code produceert nu waarschuwing C4132:

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

Als u de fout wilt oplossen, wijst u een waarde toe aan Value.

Lege declaraties

Visual Studio 2017 versie 15.3 waarschuwt nu voor lege declaraties voor alle typen, niet alleen ingebouwde typen. De volgende code produceert nu een waarschuwing op niveau 2 C4091 voor alle vier de declaraties:

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

Als u de waarschuwingen wilt verwijderen, markeert u opmerkingen of verwijdert u de lege declaraties. In gevallen waarin het niet-benoemde object bedoeld is om een neveneffect (zoals RAII) te hebben, moet het een naam krijgen.

De waarschuwing wordt uitgesloten onder /Wv:18 en is standaard ingeschakeld onder waarschuwingsniveau W2.

std::is_convertible voor matrixtypen

Vorige versies van de compiler gaven onjuiste resultaten voor std::is_convertible matrixtypen. Dit vereiste dat bibliotheekschrijvers uitzonderingen moesten maken voor de Microsoft C++-compiler bij het gebruik van het std::is_convertible<...> typekenmerk. In het volgende voorbeeld slagen de static asserts in eerdere versies van Visual Studio, maar mislukken in Visual Studio 2017 versie 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> wordt berekend door te controleren of een imaginaire functiedefinitie goed is gevormd:

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

Privédestructors en std::is_constructible

Eerdere versies van de compiler negeerden of een destructor privé was bij het bepalen van de uitkomst van std::is_constructible. Het beschouwt ze nu. In het volgende voorbeeld slagen de statische controles in eerdere versies van Visual Studio, maar mislukken in Visual Studio 2017 versie 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);

Privédestructors zorgen ervoor dat een type niet-constructeerbaar is. std::is_constructible<T, Args...> wordt berekend alsof de volgende verklaring is geschreven:

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

Deze aanroep impliceert een destructoraanroep.

C2668: Ambigu overbelastingsresolutie

Eerdere versies van de compiler konden soms geen dubbelzinnigheid detecteren wanneer meerdere kandidaten zijn gevonden via zowel declaraties als argumentafhankelijke zoekopdrachten. Deze fout kan ertoe leiden dat de verkeerde overbelasting wordt gekozen en tot onverwacht runtimegedrag. In het volgende voorbeeld geeft Visual Studio 2017 versie 15.3 C2668 correct:

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
}

Als u de code wilt herstellen, verwijdert u de using-instructie N::f indien u van plan bent om ::f() aan te roepen.

C2660: declaraties van lokale functies en argumentafhankelijke zoekacties

Declaraties van lokale functies verbergen de functiedeclaratie in de insluitende scope en schakelen argumentafhankelijke opzoeking uit. Eerdere versies van de compiler hebben in dit geval altijd argumentafhankelijke zoekopdrachten uitgevoerd. Het kan leiden tot onverwacht runtimegedrag als de compiler de verkeerde overbelasting heeft gekozen. Normaal gesproken is de fout het gevolg van een onjuiste handtekening van de declaratie van de lokale functie. In het volgende voorbeeld verhoogt Visual Studio 2017 versie 15.3 C2660 correct:

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);
}

U kunt het probleem oplossen door de f(S) handtekening te wijzigen of te verwijderen.

C5038: volgorde van initialisatie in initialisatielijsten

Klasleden worden geïnitialiseerd in de volgorde waarin ze worden gedeclareerd, niet in de volgorde waarin ze worden weergegeven in initialisatielijsten. Eerdere versies van de compiler hebben niet gewaarschuwd wanneer de volgorde van de initialisatielijst verschilt van de volgorde van de declaratie. Dit probleem kan leiden tot niet-gedefinieerd runtimegedrag als de initialisatie van een lid afhankelijk is van een ander lid in de lijst dat al wordt geïnitialiseerd. In het volgende voorbeeld genereert Visual Studio 2017 versie 15.3 (met /Wall) waarschuwing 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;
};

Als u het probleem wilt oplossen, rangschikt u de initialisatielijst in dezelfde volgorde als de declaraties. Er wordt een vergelijkbare waarschuwing weergegeven wanneer een of beide initializers verwijzen naar leden van de basisklasse.

Deze waarschuwing is standaard uitgeschakeld en is alleen van invloed op code die is gecompileerd met /Wall.

Conformiteitsverbeteringen in 15.5

Functies die zijn gemarkeerd met [14] zijn voorwaardelijke beschikbaar, zelfs in /std:c++14 de modus.

Nieuwe compilerswitch voor extern constexpr

In eerdere versies van Visual Studio gaf de compiler altijd een interne koppeling met variabelen constexpr , zelfs wanneer de variabele werd gemarkeerd extern. In Visual Studio 2017 versie 15.5 maakt een nieuwe compilerswitch, /Zc:externConstexpr, correct en normconform gedrag mogelijk. Voor meer informatie, zie extern constexpr koppeling.

Dynamische uitzonderingsspecificaties verwijderen

P0003R5 Dynamische uitzonderingsspecificaties zijn afgeschaft in C++11. De functie wordt verwijderd uit C++17, maar de (nog) afgeschafte throw() specificatie wordt strikt bewaard als een alias voor noexcept(true). Zie Dynamische uitzonderingsspecificatie verwijderen en noexceptvoor meer informatie.

not_fn()

not_fn P0005R4 is een vervanging van not1 en not2.

Herformulering enable_shared_from_this

enable_shared_from_this P0033R1 is toegevoegd in C++11. De C++17-standaard heeft de specificatie bijgewerkt om bepaalde randgevallen beter af te handelen. [14]

Samenvoegingskaarten en sets

P0083R3 Met deze functie kunt u knooppunten extraheren uit associatieve containers (dat wil gezegd, mapset, unordered_map, ) unordered_setdie vervolgens kunnen worden gewijzigd en ingevoegd in dezelfde container of een andere container die hetzelfde knooppunttype gebruikt. (Een veelvoorkomende use-case is het extraheren van een knooppunt uit een std::map, het wijzigen van de sleutel en het opnieuw invoegen.)

De onderdelen van de vestigiële bibliotheek worden afgeschaft

P0174R2 Verschillende functies van de C++-standaardbibliotheek zijn in de loop der jaren vervangen door nieuwere functies, of zijn niet nuttig of problematisch gevonden. Deze functies zijn officieel afgeschaft in C++17.

Ondersteuning voor allocator verwijderen in std::function

P0302R1 Vóór C++17 had de klassesjabloon std::function verschillende constructors die een allocator-argument namen. Het gebruik van allocators in deze context was echter problematisch en de semantiek was onduidelijk. De problematische constructors zijn verwijderd.

Oplossingen voor not_fn()

P0358R1 Nieuwe formulering biedt std::not_fn ondersteuning voor het doorgeven van de waardecategorie bij gebruik in een wrapper-aanroep.

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

P0414R2 Wijzigingen van Bibliotheekfundamentals samenvoegen met shared_ptr C++17. [14]

Oplossing shared_ptr voor matrices

P0497R0 Oplossingen voor shared_ptr ondersteuning voor arrays. [14]

Verduidelijking insert_return_type

P0508R0 De associatieve containers met unieke sleutels en de niet-geordende containers met unieke sleutels hebben een lidfunctie insert die een genest type insert_return_typeretourneert. Dat retourtype wordt nu gedefinieerd als specialisatie van een type dat wordt geparameteriseerd op de Iterator en NodeType van de container.

Inlinevariabelen voor de standaardbibliotheek

Voor P0607R0 worden verschillende algemene variabelen die in de standaardbibliotheek zijn gedeclareerd, nu inline gedeclareerd.

Bijlage D-functies afgeschaft

Bijlage D van de C++-norm bevat alle functies die zijn afgeschaft, met inbegrip shared_ptr::unique()van , <codecvt>en namespace std::tr1. Wanneer de /std:c++17 of latere compileroptie is ingesteld, worden bijna alle functies van de standaardbibliotheek in bijlage D gemarkeerd als afgekeurd. Zie De standaardbibliotheekfuncties in bijlage D zijn gemarkeerd als afgeschaft voor meer informatie.

De std::tr2::sys naamruimte in <experimental/filesystem> verzendt nu standaard een verouderingswaarschuwing onder /std:c++14, en wordt standaard verwijderd onder /std:c++17 en later.

Verbeterde conformiteit in <iostream> door een niet-standaardextensie te vermijden (expliciete klassespecialisaties).

De standaardbibliotheek maakt nu intern gebruik van variabele sjablonen.

De standaardbibliotheek is bijgewerkt als reactie op wijzigingen in de C++17-compiler. Updates omvatten de toevoeging van noexcept aan het typesysteem, en het verwijderen van dynamische-exceptiespecificaties.

Wijziging van gedeeltelijke volgorde

De compiler weigert nu de volgende code correct en geeft het juiste foutbericht:

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*)'

Het probleem in het bovenstaande voorbeeld is dat er twee verschillen zijn in de types (const versus niet-const en pack versus niet-pack). Verwijder een van de verschillen om de compilerfout te elimineren. Vervolgens kan de compiler de functies ondubbelzinnig orden.

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

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

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

Uitzonderingshandlers

Handlers van verwijzing naar matrix- of functietype zijn nooit een overeenkomst voor een uitzonderingsobject. De compiler volgt deze regel nu correct en geeft een waarschuwing op niveau 4, C4843. Het komt ook niet meer overeen met een handler van char* of wchar_t* een letterlijke tekenreeks wanneer /Zc:strictStrings deze wordt gebruikt.

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

De volgende code voorkomt de fout:

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

std::tr1 naamruimte is verouderd verklaard

De niet-standaardnaamruimte std::tr1 is nu gemarkeerd als afgeschaft in zowel C++14- als C++17-modi. In Visual Studio 2017 versie 15.5 veroorzaakt de volgende code C4996:

#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.

Verwijder de verwijzing naar de tr1 naamruimte om de fout op te lossen:

#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;
}

Standaardbibliotheekfuncties in bijlage D zijn gemarkeerd als afgeschaft

Wanneer de /std:c++17 modus of latere compilerswitch is ingesteld, worden bijna alle standaardbibliotheekfuncties in bijlage D gemarkeerd als afgeschaft.

In Visual Studio 2017 versie 15.5 geeft de volgende code C4996:

#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.

Volg de instructies in de waarschuwingstekst om de fout op te lossen, zoals wordt weergegeven in de volgende code:

#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");

Onverwijste lokale variabelen

In Visual Studio 15.5 wordt in meer gevallen waarschuwing C4189 verzonden, zoals wordt weergegeven in de volgende code:

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

Verwijder de ongebruikte variabele om de fout op te lossen.

Opmerkingen met één regel

In Visual Studio 2017 versie 15.5 worden waarschuwingen C4001 en C4179 niet meer verzonden door de C-compiler. Voorheen werden ze alleen verzonden onder de /Za compilerswitch. De waarschuwingen zijn niet meer nodig omdat opmerkingen met één regel deel uitmaken van de C-standaard sinds C99.

/* 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'

Wanneer de code niet achterwaarts compatibel hoeft te zijn, vermijd de waarschuwing door het verwijderen van de C4001- en C4179-onderdrukkingen. Als de code achterwaarts compatibel moet zijn, onderdrukt u alleen C4619.

/* C only */

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

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

__declspec kenmerken met extern "C" koppeling

In eerdere versies van Visual Studio negeerde de compiler __declspec(...)-kenmerken wanneer __declspec(...) vóór de extern "C"-koppelingsspecificatie werd toegepast. Dit gedrag heeft ertoe geleid dat code wordt gegenereerd die de gebruiker niet van plan was, met mogelijke runtime-gevolgen. De C4768-waarschuwing is toegevoegd in Visual Studio versie 15.3, maar is standaard uitgeschakeld. In Visual Studio 2017 versie 15.5 is de waarschuwing standaard ingeschakeld.

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

Als u de fout wilt oplossen, plaatst u de koppelingsspecificatie vóór het kenmerk __declspec:

extern "C" __declspec(noinline) HRESULT __stdcall

Deze nieuwe waarschuwing C4768 wordt gegeven op sommige Windows SDK-headers die zijn verzonden met Visual Studio 2017 15.3 of ouder (bijvoorbeeld: versie 10.0.15063.0, ook wel bekend als RS2 SDK). Latere versies van Windows SDK-headers (met name ShlObj.h en ShlObj_core.h) zijn echter opgelost, zodat ze de waarschuwing niet produceren. Wanneer u deze waarschuwing ziet die afkomstig is van Windows SDK-headers, kunt u deze acties uitvoeren:

  1. Schakel over naar de nieuwste Windows SDK die is geleverd met Visual Studio 2017 versie 15.5.

  2. Schakel de waarschuwing uit bij het #include van het Windows SDK-headerbestand:

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

extern constexpr Koppeling

In eerdere versies van Visual Studio gaf de compiler altijd een interne koppeling met variabelen constexpr , zelfs wanneer de variabele werd gemarkeerd extern. In Visual Studio 2017 versie 15.5 maakt een nieuwe compilerswitch (/Zc:externConstexpr) het juiste, standaardenconforme gedrag mogelijk. Uiteindelijk wordt dit gedrag de standaardinstelling.

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

Als een headerbestand een variabele bevat die is gedeclareerd extern constexpr, moet het worden gemarkeerd __declspec(selectany) om de dubbele declaraties correct te laten combineren:

extern constexpr __declspec(selectany) int x = 10;

typeid kan niet worden gebruikt voor onvolledig klassetype

In eerdere versies van Visual Studio heeft de compiler de volgende code ten onrechte toegestaan, wat resulteert in mogelijk onjuiste typegegevens. In Visual Studio 2017 versie 15.5, geeft de compiler correct een foutmelding:

#include <typeinfo>

struct S;

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

std::is_convertible doeltype

std::is_convertible vereist dat het doeltype een geldig retourtype is. In eerdere versies van Visual Studio heeft de compiler abstracte typen onjuist toegestaan, wat kan leiden tot een onjuiste overbelastingsresolutie en onbedoeld runtimegedrag. Met de volgende code wordt nu C2338 correct opgehaald:

#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

Om de fout te voorkomen, moet u bij het gebruik is_convertible van aanwijzertypen vergelijken omdat een vergelijking van niet-aanwijzertypen kan mislukken als één type abstract is:

#include <type_traits>

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

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

Dynamische uitzonderingsspecificatie verwijderen en noexcept

In C++17 is throw() een alias voor noexcept, throw(<type list>) en throw(...) worden verwijderd, en bepaalde typen kunnen noexcept omvatten. Deze wijziging kan problemen met broncodecompatibiliteit veroorzaken met code die voldoet aan C++14 of eerder. De /Zc:noexceptTypes- switch kan worden gebruikt om, terwijl de C++17-modus algemeen wordt gebruikt, terug te keren naar de C++14-versie van noexcept. Hiermee kunt u uw broncode bijwerken om te voldoen aan C++17 zonder dat u al uw throw() code tegelijk hoeft te herschrijven.

De compiler constateert nu ook meer niet-overeenkomende exceptiespecificaties in declaraties in de C++17-modus of met /permissive- als nieuwe waarschuwing C5043.

Met de volgende code worden C5043 en C5040 gegenereerd in Visual Studio 2017 versie 15.5 wanneer de /std:c++17 switch wordt toegepast:

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
};

Als u de fouten wilt verwijderen terwijl u nog steeds /std:c++17 gebruikt, voegt u de /Zc:noexceptTypes- schakeloptie toe aan de opdrachtregel of werkt u uw code bij om noexcept te gebruiken, zoals in het volgende voorbeeld wordt weergegeven.

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

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

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

Inlinevariabelen

Statische constexpr gegevensleden zijn nu impliciet inline, wat betekent dat hun declaratie binnen een klasse nu de definitie is. Het gebruik van een out-of-line-definitie voor een static constexpr gegevenslid is overbodig en is nu afgeschaft. Wanneer in Visual Studio 2017 versie 15.5 de /std:c++17-schakelaar wordt toegepast, produceert de volgende code nu waarschuwing 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(...) waarschuwing C4768 nu standaard ingeschakeld

De waarschuwing is toegevoegd in Visual Studio 2017 versie 15.3, maar is standaard uitgeschakeld. In Visual Studio 2017 versie 15.5 is de waarschuwing standaard ingeschakeld. Zie nieuwe waarschuwing voor __declspec kenmerken voor meer informatie.

Standaardfuncties en __declspec(nothrow)

De compiler heeft eerder toegestaan dat standaardfuncties worden gedeclareerd met __declspec(nothrow) wanneer de bijbehorende basis-/lidfuncties uitzonderingen toestaan. Dit gedrag is in strijd met de C++-standaard en kan tijdens runtime niet-gedefinieerd gedrag veroorzaken. Volgens de standaard dienen dergelijke functies als verwijderd te worden gedefinieerd indien er sprake is van een discrepantie in de uitzonderingsspecificatie. Onder /std:c++17 wordt de volgende code C2280 opgeleverd:

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.
}

Als u deze code wilt corrigeren, verwijdert u __declspec(nothrow) uit de standaardfunctie of verwijdert = default en geeft u een definitie op voor de functie, samen met de vereiste afhandeling van uitzonderingen:

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 en gedeeltelijke specialisaties

Met noexcept in het typesysteem kunnen gedeeltelijke specialisaties voor het vergelijken van bepaalde 'aanroepbare' typen niet compileren of de primaire sjabloon niet kiezen, vanwege een ontbrekende gedeeltelijke specialisatie voor pointers-to-noexcept-functions.

In dergelijke gevallen moet u mogelijk meer gedeeltelijke specialisaties toevoegen om de noexcept functieaanwijzers en noexcept aanwijzers naar lidfuncties te behandelen. Deze overbelastingen zijn alleen toegestaan in de /std:c++17-modus of later. Als compatibiliteit met eerdere versies met C++14 moet worden gehandhaafd en u code schrijft die anderen gebruiken, moet u deze nieuwe overbelastingen in #ifdef richtlijnen bewaken. Als u in een zelfstandige module werkt, kunt u in plaats van #ifdef bewakers gewoon compileren met de /Zc:noexceptTypes- switch.

De volgende code compileert onder /std:c++14 maar mislukt met /std:c++17 fout C2027:

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>'
}

De volgende code slaagt onder /std:c++17 omdat de compiler de nieuwe gedeeltelijke specialisatie A<void (*)() noexcept>kiest:

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
}

Verbeteringen in conformiteit in 15.6

Basisprincipes van C++17-bibliotheek V1

P0220R1 incorporeert de Technische Specificatie van de fundamentals van bibliotheken voor C++17 in de standaard. Behandelt updates voor <experimental/tuple>, <experimental/optional>, , <experimental/functional>, <experimental/any>, <experimental/string_view>, , <experimental/memory>, <experimental/memory_resource>en <experimental/algorithm>.

C++17: Argumentafleiding van klassesjablonen voor de standaardbibliotheek verbeteren

P0739R0 Verplaats adopt_lock_t naar de voorkant van de parameterlijst van scoped_lock om consistent gebruik van scoped_lock mogelijk te maken. Toestaan dat std::variant constructor in meer gevallen aan overbelastingoplossing kan deelnemen om kopieertoewijzing mogelijk te maken.

Conformiteitsverbeteringen in 15.7

C++17: Herformulering van overervende constructors

P0136R1 geeft aan dat een using declaratie die een constructor noemt, nu de bijbehorende basisklasseconstructors zichtbaar maakt voor initialisaties van de afgeleide klasse, in plaats van meer afgeleide klasseconstructors te declareren. Deze herwording is een wijziging van C++14. In Visual Studio 2017 versie 15.7 en hoger, in /std:c++17 de modus en hoger, is code die geldig is in C++14 en die gebruikmaakt van overnemende constructors mogelijk niet geldig of kan andere semantiek hebben.

In het volgende voorbeeld ziet u het gedrag van C++14:

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).

In het volgende voorbeeld wordt het gedrag van /std:c++17 in Visual Studio 15.7 getoond.

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)

Zie Constructors voor meer informatie.

C++17: Uitgebreide initialisatie van aggregaties

P0017R1

Als de constructor van een basisklasse niet openbaar is, maar toegankelijk is voor een afgeleide klasse, kunt u onder /std:c++17 de modus en hoger in Visual Studio 2017 versie 15.7 geen lege accolades meer gebruiken om een object van het afgeleide type te initialiseren. In het volgende voorbeeld ziet u het gedrag dat voldoet aan 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 Derived wordt nu beschouwd als een aggregatietype. Dit betekent dat de initialisatie van Base via de privé-standaardconstructor rechtstreeks plaatsvindt, als onderdeel van de uitgebreide initialisatieregel voor aggregaties. Voorheen werd de Base privéconstructor aangeroepen via de Derived constructor en is het gelukt vanwege de declaratie van de vriend. In het volgende voorbeeld ziet u het gedrag van C++17 in Visual Studio versie 15.7 in /std:c++17 de modus:

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: niet-type sjabloonparameters declareren met auto

P0127R2

In /std:c++17 de modus kan de compiler nu het type van een niet-type sjabloonargument afleiden dat is gedeclareerd met auto:

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

Een effect van deze nieuwe functie is dat geldige C++14-code mogelijk niet geldig is of andere semantiek heeft. Sommige overbelastingen die eerder ongeldig waren, zijn nu bijvoorbeeld geldig. In het volgende voorbeeld ziet u C++14-code die wordt gecompileerd omdat de aanroep naar example(p) is gebonden example(void*);. In Visual Studio 2017 versie 15.7, in /std:c++17 modus is de example functiesjabloon de beste overeenkomst.

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
}

In het volgende voorbeeld ziet u C++17-code in Visual Studio 15.7 in /std:c++17 de modus:

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: Elementaire tekenreeksconversies (gedeeltelijk)

P0067R5 Functies op laag niveau, onafhankelijk van landinstellingen voor conversies tussen gehele getallen en tekenreeksen en tussen getallen met drijvende komma en tekenreeksen.

C++20: Onnodig verval voorkomen (gedeeltelijk)

P0777R1 Voegt differentiatie toe tussen het begrip "verval" en die van het simpelweg verwijderen van const of verwijzingskwalificaties. Nieuwe typekenmerk remove_reference_t vervangt decay_t in sommige contexten. Ondersteuning voor remove_cvref_t wordt geïmplementeerd in Visual Studio 2019.

C++17: Parallelle algoritmen

P0024R2 De Parallelisme-TS is opgenomen in de standaard, met kleine wijzigingen.

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

P0030R1 Voegt drie nieuwe overbelastingen toe aan std::hypot, voor typen float, doubleen long double, die elk drie invoerparameters hebben.

C++17: <filesystem>

P0218R1 Neemt het bestandssysteem TS in de standaard met enkele formuleringswijzigingen.

C++17: Wiskundige speciale functies

P0226R1 Neemt eerdere technische specificaties voor wiskundige speciale functies op in de standaard <cmath> header.

C++17: Aftrekhandleidingen voor de standaardbibliotheek

P0433R2 Updates voor STL om te profiteren van C++17 acceptatie van P0091R3, waarmee ondersteuning wordt toegevoegd voor de aftrek van klassesjabloonargumenten.

C++17: Elementaire tekenreeksconversies herstellen

P0682R1 Verplaats de nieuwe elementaire tekenreeksconversiefuncties van P0067R5 naar een nieuwe header <charconv> en breng andere verbeteringen aan, waaronder het wijzigen van foutafhandeling voor gebruik std::errc in plaats van std::error_code.

C++17: constexpr voor char_traits (gedeeltelijk)

P0426R1 Wijzigingen in std::traits_type lidfuncties length, compare en find om std::string_view bruikbaar te maken in constante expressies. (In Visual Studio 2017 versie 15.6 wordt alleen ondersteund voor Clang/LLVM. In versie 15.7 is de ondersteuning ook bijna voltooid voor ClXX.)

C++17: Standaardargument in de primaire klasse-sjabloon

Deze gedragswijziging is een voorwaarde voor P0091R3 - Template argument deduction for class templates.

Voorheen negeerde de compiler het standaardargument in de primaire klassesjabloon:

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

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

In /std:c++17 de modus in Visual Studio 2017 versie 15.7 wordt het standaardargument niet genegeerd:

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

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

Afhankelijke naamomzetting

Deze gedragswijziging is een voorwaarde voor P0091R3 - Template argument deduction for class templates.

In het volgende voorbeeld zet de compiler in Visual Studio 15.6 en eerder D::type om naar B<T>::type in de primaire klassesjabloon.

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

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

Visual Studio 2017 versie 15.7, in /std:c++17 de modus, vereist het typename trefwoord in de using instructie in D. Zonder typename, de compiler genereert waarschuwing C4346: 'B<T*>::type': dependent name is not a type en fout C2061: syntax error: identifier 'type':

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

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

C++17: [[nodiscard]] kenmerk - verhoging van waarschuwingsniveau

In Visual Studio 2017 versie 15.7 in /std:c++17 de modus wordt het waarschuwingsniveau van C4834 verhoogd van W3 naar W1. U kunt de waarschuwing uitschakelen met een cast naar void, of door /wd:4834 aan de compiler door te geven.

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

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

Initialisatielijst voor variadic sjabloonconstructorbasisklasse

In eerdere edities van Visual Studio werd een initializer-lijst voor een basisklasse met een variadische sjabloonconstructor ten onrechte toegelaten zonder foutmelding, zelfs als er sjabloonargumenten ontbraken. In Visual Studio 2017 versie 15.7 wordt een compilerfout gegenereerd.

In het volgende codevoorbeeld in Visual Studio 2017 versie 15.7 wordt fout C2614 gegenereerd:

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;

Als u de fout wilt oplossen, wijzigt u de B() expressie in B<T>().

constexpr initialisatie van aggregaties

Eerdere versies van de C++-compiler hebben de initialisatie van aggregaties onjuist verwerkt constexpr . De compiler heeft ongeldige code geaccepteerd waarin de aggregate-init-list te veel elementen had en er ongeldige objectcode voor heeft geproduceerd. De volgende code is een voorbeeld van deze code:

#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 versie 15.7 update 3 en hoger veroorzaakt het vorige voorbeeld nu een C2078-fout. In het volgende voorbeeld ziet u hoe u de code kunt herstellen. Wanneer u een std::array met geneste accolade-init-lijsten initialiseert, geeft u de binnenste matrix een eigen accoladelijst:

#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;
}

Conformiteitsverbeteringen in 15.8

typename op niet-gekwalificeerde identificatoren

In /permissive- de modus worden valse typename trefwoorden voor niet-gekwalificeerde id's in aliassjabloondefinities niet meer geaccepteerd door de compiler. De volgende code produceert nu C7511:

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

Als u de fout wilt oplossen, wijzigt u de tweede regel in using X = T;.

__declspec() aan de rechterkant van aliassjabloondefinities

__declspec is niet meer toegestaan aan de rechterkant van een aliassjabloondefinitie. Voorheen heeft de compiler deze code geaccepteerd, maar genegeerd. Het zou nooit resulteren in een afschrijvingswaarschuwing wanneer de alias wordt gebruikt.

Het standaard C++-kenmerk [[deprecated]] kan in plaats daarvan worden gebruikt en wordt gerespecteerd in Visual Studio 2017 versie 15.6. De volgende code produceert nu C2760:

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

Als u de fout wilt oplossen, wijzigt u de code in het volgende (met het kenmerk dat vóór de =van de aliasdefinitie wordt geplaatst):

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

Diagnostiek voor naamopzoeking in twee fasen

Zoeken in twee fasen vereist dat niet-afhankelijke namen die in sjabloonlichamen worden gebruikt, zichtbaar zijn voor de sjabloon ten tijde van de definitie. Voorheen liet de Microsoft C++-compiler een niet-gevonden naam ongemoeid totdat deze werd opgezocht bij het moment van instantiering. Nu is het vereist dat niet-afhankelijke namen gebonden zijn in de hoofdtekst van de sjabloon.

Een manier waarop dit kan worden gemanifesteerd, is met opzoeken in afhankelijke basisklassen. Voorheen heeft de compiler het gebruik toegestaan van namen die zijn gedefinieerd in afhankelijke basisklassen. Dit komt doordat ze tijdens de instantieringstijd worden opgezocht wanneer alle typen zijn opgelost. Nu die code wordt behandeld als een fout. In dergelijke gevallen kunt u afdwingen dat de variabele wordt opgezoekd bij instantiëringstijd door deze te kwalificeren met het type basisklasse of op een andere manier afhankelijk te maken, bijvoorbeeld door een this-> aanwijzer toe te voegen.

In de /permissive--modus genereert de volgende code nu C3861:

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
    }
};

Als u de fout wilt oplossen, wijzigt u de return instructie in return this->base_value;.

Opmerking

In boost.Python-bibliotheekversies vóór 1.70 is er een MSVC-specifieke tijdelijke oplossing voor een declaratie voor het doorsturen van sjablonen in unwind_type.hpp. In de modus /permissive- vanaf Visual Studio 2017 versie 15.8 (_MSC_VER==1915) voert de MSVC-compiler de argumentafhankelijke naamzoekactie (ADL) correct uit. Het is nu consistent met andere compilers, waardoor deze tijdelijke oplossing niet nodig is. Werk uw Boost.Python-bibliotheek bij om fout C3861: 'unwind_type': identifier not foundte voorkomen.

declaraties en definities doorsturen in naamruimte std

Met de C++-standaard kan een gebruiker geen doorstuurdeclaraties of definities toevoegen aan naamruimte std. Het toevoegen van declaraties of definities aan naamruimte std of aan een naamruimte binnen de naamruimte std resulteert nu in niet-gedefinieerd gedrag.

Op een bepaald moment in de toekomst verplaatst Microsoft de locatie waar sommige standaardbibliotheektypen worden gedefinieerd. Met deze wijziging wordt de bestaande code verbroken waarmee doorstuurdeclaraties worden toegevoegd aan de naamruimte std. Een nieuwe waarschuwing, C4643, helpt dergelijke bronproblemen te identificeren. De waarschuwing is ingeschakeld in /default de modus en is standaard uitgeschakeld. Het heeft invloed op programma's die zijn gecompileerd met /Wall of /WX.

Met de volgende code wordt nu C4643 opgehaald:

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

Als u de fout wilt oplossen, gebruikt u een #include richtlijn in plaats van een doorstuurdeclaratie:

#include <vector>

Constructors die aan zichzelf delegeren

De C++-standaard stelt voor dat een compiler een diagnose moet verzenden wanneer een delegerende constructor aan zichzelf delegert. De Microsoft C++-compiler in /std:c++17 en /std:c++latest modus geeft nu C7535.

Zonder deze fout wordt het volgende programma gecompileerd, maar wordt er een oneindige lus gegenereerd:

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

Als u de oneindige lus wilt voorkomen, delegeert u deze naar een andere constructor:

class X {
public:

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

offsetof met constante expressies

offsetof is traditioneel geïmplementeerd met behulp van een macro waarvoor een reinterpret_cast. Dit gebruik is illegaal in contexten waarvoor een constante expressie is vereist, maar de Microsoft C++-compiler heeft dit traditioneel toegestaan. De offsetof macro die als onderdeel van de standaardbibliotheek wordt verzonden, maakt correct gebruik van een intrinsiek compiler (__builtin_offsetof), maar veel mensen hebben de macro-truc gebruikt om hun eigen offsetofte definiëren.

In Visual Studio 2017 versie 15.8 beperkt de compiler de gebieden die deze reinterpret_cast operators kunnen weergeven in de standaardmodus, zodat code voldoet aan het standaardgedrag van C++ . Onder /permissive-, de beperkingen zijn nog strenger. Het gebruik van het resultaat van een offsetof op plaatsen waarvoor constante expressies zijn vereist, kan resulteren in code die waarschuwing C4644 of C2975 uitgeeft.

Met de volgende code wordt C4644 in de standaardmodus en /std:c++17-modus, en C2975 in de /permissive--modus geactiveerd:

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;
    }
}

** Gebruik offsetof zoals gedefinieerd via <cstddef> om de fout op te lossen.

#include <cstddef>

struct Data {
    int x;
};

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

cv-kwalificaties bij basisklassen die onderhevig zijn aan pack-uitbreiding

In eerdere versies van de Microsoft C++-compiler werd niet gedetecteerd dat een basisklasse cv-kwalificaties had als deze ook onderhevig was aan pack uitbreiding.

In Visual Studio 2017 versie 15.8, in modus /permissive-, geeft de volgende code fout C3770:

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 trefwoord- en geneste naamaanduidingen

In de /permissive--modus vereist de compiler nu dat het template trefwoord voorafgaat aan een sjabloonnaam wanneer het komt na een afhankelijke geneste naam-specifier.

Met de volgende code in de /permissive--modus levert nu C7510 op:

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
    }
};

Als u de fout wilt oplossen, voegt u het template trefwoord toe aan de Base<T>::example<int>(); instructie, zoals wordt weergegeven in het volgende voorbeeld:

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>();
    }
};

Conformiteitsverbeteringen in 15.9

Evaluatievolgorde van links naar rechts voor operators->*, []en >><<

Vanaf C++17 moeten de operanden van de operators ->*[], >>en << worden geëvalueerd in volgorde van links naar rechts. Er zijn twee gevallen waarin de compiler deze volgorde niet kan garanderen:

  • wanneer een van de operand-expressies een object is dat wordt doorgegeven door een waarde of een object bevat dat wordt doorgegeven door een waarde, of

  • wanneer er wordt gecompileerd met behulp van /clr, en een van de operanden een veld van een object of een array-element is.

De compiler verzendt waarschuwing C4866 wanneer de evaluatie van links naar rechts niet kan worden gegarandeerd. De compiler genereert deze waarschuwing alleen als /std:c++17 of later is opgegeven, omdat de vereiste van links naar rechts van deze operators is geïntroduceerd in C++17.

Als u deze waarschuwing wilt oplossen, moet u eerst overwegen of de evaluatie van de operanden van links naar rechts noodzakelijk is. Het kan bijvoorbeeld nodig zijn wanneer de evaluatie van de operanden orderafhankelijke bijwerkingen kan produceren. De volgorde waarin operanden worden geëvalueerd, heeft in veel gevallen geen waarneembaar effect. Als de volgorde van evaluatie van links naar rechts moet zijn, overweeg dan of u de operanden via een constante verwijzing kunt doorgeven. Deze wijziging elimineert de waarschuwing in het volgende codevoorbeeld:

// 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>>
};

Id's in aliassjablonen voor leden

Een id die wordt gebruikt in een aliassjabloondefinitie voor leden, moet worden gedeclareerd voordat u deze gebruikt.

In eerdere versies van de compiler is de volgende code toegestaan. In Visual Studio 2017 versie 15.9, in /permissive- de modus, haalt de compiler C3861 op:

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;

Om de fout te verhelpen, declareer from_template vóór from_template_t.

Wijzigingen in modules

In Visual Studio 2017, versie 15.9, verhoogt de compiler C5050 wanneer de opdrachtregelopties voor modules niet consistent zijn tussen de kant van het maken van modules en het verbruik van de module. In het volgende voorbeeld zijn er twee problemen:

  • Aan de verbruikszijde (main.cpp) is de optie /EHsc niet opgegeven.

  • De C++-versie bevindt zich /std:c++17 aan de creatiezijde en /std:c++14 aan de consumptiezijde.

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

De compiler verhoogt C5050 voor beide gevallen:

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

De compiler haalt ook C7536 op wanneer er met het .ifc bestand is geknoeid. De header van de module-interface bevat een SHA2-hash van de inhoud eronder. Bij het importeren wordt het .ifc bestand gehasht en vervolgens gecontroleerd op de hash die is opgegeven in de header. Als deze niet overeenkomen, wordt fout C7536 gegenereerd:

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

Gedeeltelijke volgorde met aliassen en niet-afgeleide contexten

Implementaties verschillen in de regels voor gedeeltelijke volgorde betreffende aliassen in niet-gededuceerde contexten. In het volgende voorbeeld veroorzaken GCC en de Microsoft C++-compiler (in /permissive- de modus) een fout, terwijl Clang de code accepteert.

#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;
}

In het vorige voorbeeld wordt C2668 veroorzaakt:

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>)'

De implementatieverschillen zijn het gevolg van een regressie in de C++-standaardformulering. De oplossing voor kernprobleem 2235 heeft tekst verwijderd waardoor deze overbelastingen kunnen worden gerangschikt. De huidige C++-standaard biedt geen mechanisme om deze functies gedeeltelijk te orden, zodat ze als dubbelzinnig worden beschouwd.

Als tijdelijke oplossing raden we u aan niet te vertrouwen op gedeeltelijke volgorde om dit probleem op te lossen. Gebruik in plaats daarvan SFINAE om bepaalde overbelastingen te verwijderen. In het volgende voorbeeld gebruiken we een helperklasse IsA om de eerste overbelasting te verwijderen wanneer Alloc het een specialisatie is van A:

#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;
}

Ongeldige expressies en niet-letterlijke typen in sjabloonfunctiedefinities

Illegale expressies en niet-letterlijke typen worden nu correct aangegeven in de definities van sjabloonfuncties die expliciet zijn gespecialiseerd. Eerder werden dergelijke fouten niet verzonden voor de functiedefinitie. De illegale expressie of het niet-letterlijke type zou echter nog steeds zijn vastgesteld als deze zou worden geëvalueerd als onderdeel van een constante uitdrukking.

In eerdere versies van Visual Studio wordt de volgende code gecompileerd zonder waarschuwing:

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 versie 15.9 genereert de code fout 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'.

Verwijder de constexpr kwalificatie uit de expliciete instantiëring van de functie f()om de fout te voorkomen.

Zie ook

Microsoft C/C++-taalconformiteit