Sdílet prostřednictvím


Vylepšení shody c++, změny chování a opravy chyb v sadě Visual Studio 2017

Microsoft C/C++ v sadě Visual Studio (MSVC) vylepšuje vylepšení shody a opravy chyb v každé verzi. Tento článek uvádí vylepšení podle hlavní verze a potom podle verze. Pokud chcete přejít přímo na změny konkrétní verze, použijte následující seznam v tomto článku.

Tento dokument obsahuje seznam změn v sadě Visual Studio 2017. Průvodce změnami v sadě Visual Studio 2022 najdete v tématu Vylepšení shody jazyka C++ v sadě Visual Studio 2022. Průvodce změnami v sadě Visual Studio 2019 najdete v tématu Vylepšení shody jazyka C++ v sadě Visual Studio 2019. Úplný seznam předchozích vylepšení shody najdete v tématu Visual C++ What's New 2003 až 2015.

Vylepšení shody v sadě Visual Studio 2017 RTW (verze 15.0)

S podporou generalizované a nestatické constexpr inicializace členů dat (NSDMI) pro agregace je teď kompilátor MSVC v sadě Visual Studio 2017 dokončen pro funkce přidané ve standardu C++14. Kompilátor ale stále nemá několik funkcí ze standardů C++11 a C++98. Aktuální stav kompilátoru najdete v souladu jazyka Microsoft C/C++.

C++11: Podpora výrazů SFINAE v dalších knihovnách

Kompilátor nadále vylepšuje podporu výrazu SFINAE. Vyžaduje se pro odpočty argumentů šablony a nahrazení, kde decltype se výrazy constexpr můžou zobrazovat jako parametry šablony. Další informace najdete v tématu Vylepšení výrazu SFINAE v sadě Visual Studio 2017 RC.

C++14: NSDMI pro agregace

Agregace je pole nebo třída, která obsahuje: žádný konstruktor poskytovaný uživatelem, žádné nestatické datové členy, které jsou soukromé nebo chráněné, žádné základní třídy a žádné virtuální funkce. Počínaje jazykem C++14 mohou agregace obsahovat inicializátory členů. Další informace najdete v tématu Inicializátory a agregace členů.

C++14: Rozšířené constexpr

Výrazy deklarované jako constexpr nyní mohou obsahovat určité druhy deklarací, příkazy if a switch, příkazy smyčky a mutaci objektů, jejichž životnost začala v rámci vyhodnocení výrazu constexpr . Už není nutné, aby constexpr nestatická členská funkce byla implicitně const. Další informace naleznete v tématu Uvolnění omezení pro constexpr funkce.

C++17: Terse static_assert

parametr zprávy je static_assert volitelný. Další informace naleznete v tématu N3928: Rozšíření static_assert, v2.

C++17: [[fallthrough]] atribut

V /std:c++17 režimu a později [[fallthrough]] lze atribut použít v kontextu příkazů switch jako nápovědu kompilátoru, že je záměrné chování. Tento atribut brání kompilátoru v vydávání upozornění v takových případech. Další informace najdete na webu P0188R0 - Wording for [[fallthrough]] attribute.

Zobecněné smyčky založené na for rozsahu

Smyčky založené na for rozsahu už nevyžadují, aby begin() a end() vracely objekty stejného typu. Tato změna umožňuje vrátit sentinel používaný rozsahy end() a range-v3 dokončené,ale ne zcela publikované technické specifikace rozsahů. Další informace najdete na webu P0184R0 - Generalizing the Range-Based for Loop.

Inicializace kopírování seznamu

Visual Studio 2017 správně vyvolává chyby kompilátoru související s vytvářením objektů pomocí seznamů inicializátorů. Tyto chyby nebyly zachyceny v sadě Visual Studio 2015 a mohly by způsobit chybové ukončení nebo nedefinované chování modulu runtime. Podle , N4594 13.3.1.7p1v copy-list-initialization, kompilátor je nutné zvážit explicitní konstruktor pro rozlišení přetížení. Pokud se však zvolí konkrétní přetížení, musí vyvolat chybu.

Následující dva příklady se kompilují v sadě Visual Studio 2015, ale ne v sadě 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 &'

}

Pokud chcete chybu opravit, použijte přímou inicializaci:

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

V sadě Visual Studio 2015 kompilátor chybně zpracovával inicializaci copy-list-inicializace stejným způsobem jako běžná inicializace kopírování: považuje se pouze za převod konstruktorů pro rozlišení přetížení. V následujícím příkladu sada Visual Studio 2015 zvolí MyInt(23). Visual Studio 2017 správně vyvolá chybu.

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

Tento příklad je podobný předchozímu, ale vyvolá jinou chybu. V sadě Visual Studio 2015 proběhne úspěšně a v sadě Visual Studio 2017 s C2668 selže.

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
}

Zastaralé definice typedef

Visual Studio 2017 teď vydává správné upozornění pro zastaralé typy deklarované ve třídě nebo struktuře. Následující příklad se zkompiluje bez upozornění v sadě Visual Studio 2015. Vytvoří C4996 v sadě 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 správně vyvolá chybu, když levý operand podmíněného vyhodnocení operace není platný v constexpr kontextu. Následující kód se zkompiluje v sadě Visual Studio 2015, ale ne v sadě Visual Studio 2017, kde vyvolá 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
}

Chcete-li chybu opravit, deklarujte array::size() funkci jako constexpr nebo odeberte constexpr kvalifikátor z f.

Typy tříd předané do variadických funkcí

V sadě Visual Studio 2017 musí být třídy nebo struktury, které jsou předány do variadické funkce, například printf musí být triviálně kopírovatelné. Když jsou takové objekty předány, kompilátor jednoduše vytvoří bitovou kopii a nevolá konstruktor ani destruktor.

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

Chcete-li chybu opravit, můžete volat členovou funkci, která vrací triviálně kopírovatelný typ,

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

nebo jinak před předáním objektu převeďte statický přetypování:

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

U řetězců vytvořených a spravovaných pomocí CStringby se zadaný operator LPCTSTR() objekt měl použít k přetypování CString objektu na ukazatel C očekávaný formátovacím řetězcem.

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

Kvalifikátory cv ve konstrukci tříd

V sadě Visual Studio 2015 kompilátor někdy nesprávně ignoruje kvalifikátor cv při generování objektu třídy prostřednictvím volání konstruktoru. Tento problém může potenciálně způsobit chybové ukončení nebo neočekávané chování modulu runtime. Následující příklad se zkompiluje v sadě Visual Studio 2015, ale v sadě Visual Studio 2017 vyvolá chybu kompilátoru:

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

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

Chcete-li chybu opravit, deklarujte operator int() jako const.

Kontrola přístupu u kvalifikovaných názvů v šablonách

Předchozí verze kompilátoru nekontrolovali přístup k kvalifikovaným názvům v některých kontextech šablony. Tento problém může kolidovat s očekávaným chováním SFINAE, kdy se očekává, že nahrazení selže kvůli nedostupnosti názvu. Mohlo by dojít k chybovému ukončení nebo neočekávanému chování za běhu, protože kompilátor nesprávně volal nesprávné přetížení operátoru. V sadě Visual Studio 2017 se vyvolá chyba kompilátoru. Konkrétní chyba se může lišit, ale typická chyba je C2672, "nebyla nalezena žádná odpovídající přetížená funkce". Následující kód se zkompiluje v sadě Visual Studio 2015, ale v sadě Visual Studio 2017 vyvolá chybu:

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

Chybějící seznamy argumentů šablony

V sadě Visual Studio 2015 a starších kompilátor nediagnostikoval všechny chybějící seznamy argumentů šablony. Všimněte si, že chybějící šablona se objevila v seznamu parametrů šablony: například když chybí část výchozího argumentu šablony nebo parametr šablony bez typu. Tento problém může mít za následek nepředvídatelné chování, včetně chyb kompilátoru nebo neočekávaného chování modulu runtime. Následující kód se zkompiluje v sadě Visual Studio 2015, ale v sadě Visual Studio 2017 dojde k chybě.

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

Aby kompilátor podporoval výraz-SFINAE, teď parsuje decltype argumenty, když jsou šablony deklarovány místo vytvoření instance. Takže pokud se v argumentu decltype najde nezávisená specializace, není odložena do doby vytvoření instance. Zpracovává se okamžitě a všechny výsledné chyby se v té době diagnostikují.

Následující příklad ukazuje takovou chybu kompilátoru, která je vyvolána v okamžiku deklarace:

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

Třídy deklarované v anonymních oborech názvů

Podle standardu C++ má třída deklarovaná uvnitř anonymního oboru názvů interní propojení a to znamená, že se nedá exportovat. V sadě Visual Studio 2015 a starších verzích se toto pravidlo nevynucovalo. V sadě Visual Studio 2017 se pravidlo vynucuje částečně. V sadě Visual Studio 2017 se v následujícím příkladu zobrazí chyba C2201:

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

Výchozí inicializátory pro členy třídy hodnot (C++/CLI)

V sadě Visual Studio 2015 a starší kompilátor povolil (ale ignoroval) výchozí inicializátor člena pro člena třídy hodnot. Výchozí inicializace třídy hodnot vždy nula inicializuje členy. Výchozí konstruktor není povolený. V sadě Visual Studio 2017 vyvolá výchozí inicializátory členů chybu kompilátoru, jak je znázorněno v tomto příkladu:

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

Výchozí indexery (C++/CLI)

V sadě Visual Studio 2015 a starším kompilátor v některých případech nesprávně identifikoval výchozí vlastnost jako výchozí indexer. Problém bylo možné obejít pomocí identifikátoru default pro přístup k vlastnosti. Samotné alternativní řešení bylo po default zavedení klíčového slova v jazyce C++11 problematické. V sadě Visual Studio 2017 byly opraveny chyby, které vyžadovaly alternativní řešení. Kompilátor nyní vyvolá chybu při default použití pro přístup k výchozí vlastnosti třídy.

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

V sadě Visual Studio 2017 můžete přistupovat k oběma vlastnostem hodnoty podle jejich názvu:

#using "class1.dll"

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

Vylepšení shody ve verzi 15.3

constexpr lambda

Výrazy lambda se teď můžou používat ve výrazech konstant. Další informace najdete v tématu constexpr výrazy lambda v jazyce C++.

if constexpr v šablonách funkcí

Šablona funkce může obsahovat if constexpr příkazy, které umožňují větvení v čase kompilace. Další informace najdete v příkazechif constexpr.

Příkazy výběru s inicializátory

Příkaz if může obsahovat inicializátor, který zavádí proměnnou v oboru bloku v samotném příkazu. Další informace najdete if v příkazech s inicializátorem.

[[maybe_unused]] a [[nodiscard]] atributy

Nový atribut [[maybe_unused]] umlčí upozornění, když se entita nepoužívá. Atribut [[nodiscard]] vytvoří upozornění, pokud je vrácená hodnota volání funkce zahozena. Další informace naleznete v tématu Atributy v jazyce C++.

Použití oborů názvů atributů bez opakování

Nová syntaxe pro povolení pouze jednoho identifikátoru oboru názvů v seznamu atributů Další informace naleznete v tématu Atributy v jazyce C++.

Strukturované vazby

Nyní je možné v jedné deklaraci uložit hodnotu s jednotlivými názvy jejích součástí, pokud je hodnota pole, nebo std::tuple std::pairnebo má všechny veřejné nestatické datové členy. Další informace najdete v tématu P0144R0 - Structured Bindings a vrácení více hodnot z funkce.

Pravidla konstrukce pro enum class hodnoty

Teď existuje implicitní převod pro vymezené výčty, které nejsou zúžené. Převede se ze základního typu výčtu s vymezeným oborem na samotný výčet. Převod je k dispozici, pokud jeho definice nezavádí enumerátor a když zdroj používá syntaxi inicializace seznamu. Další informace naleznete v tématu P0138R2 - Construction Rules for enum class Values a výčty.

Zaznamenání *this podle hodnoty

Objekt *this ve výrazu lambda je teď možné zachytit hodnotou. Tato změna umožňuje scénáře, ve kterých je lambda vyvolána paralelně a asynchronními operacemi, zejména v novějších architekturách počítačů. Další informace najdete na webu P0018R3 - Lambda Capture of *this by Value as [=,*this].

Odebrání operator++ pro bool

operator++ u typů se už nepodporuje bool . Další informace najdete na webu P0002R1 - Remove Deprecated operator++(bool).

Odebrání zastaralého klíčového register slova

Klíčové register slovo, dříve zastaralé (a ignorováno kompilátorem), je nyní odebráno z jazyka. Další informace najdete na webu P0001R1 - Remove Deprecated Use of the register Keyword.

Volání odstraněných šablon členů

V předchozích verzích sady Visual Studio by kompilátor v některých případech nedokázal vygenerovat chybu pro chybně vytvořená volání odstraněné členské šablony. Tato volání by potenciálně způsobila chybové ukončení za běhu. Následující kód teď vytvoří 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
}

Chcete-li chybu opravit, deklarujte i jako int.

Kontroly před podmínkou pro vlastnosti typu

Visual Studio 2017 verze 15.3 vylepšuje kontroly před podmínkou pro typové vlastnosti, aby přesněji dodržovaly standard. Jednou z těchto kontrol je přiřazení. Následující kód vytvoří C2139 v sadě Visual Studio 2017 verze 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

Upozornění nového kompilátoru a kontroly za běhu při zařazování nativních na spravovanou správu

Volání ze spravovaných funkcí do nativních funkcí vyžaduje zařazování. CLR zařazování provede, ale nerozumí sémantice jazyka C++. Pokud předáte nativní objekt podle hodnoty, CLR buď volá konstruktor kopírování objektu nebo používá BitBlt, což může způsobit nedefinované chování za běhu.

Kompilátor teď vygeneruje upozornění, pokud najde tuto chybu v době kompilace: nativní objekt s odstraněnou kopií ctor se předává mezi nativní a spravovanou hranici podle hodnoty. V případech, kdy kompilátor v době kompilace nezná, vloží kontrolu za běhu, aby program okamžitě volal std::terminate , když dojde k zařazování ve špatném formátu. V sadě Visual Studio 2017 verze 15.3 vytvoří následující kód upozornění 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.`
}

Chcete-li chybu opravit, odeberte direktivu #pragma managed pro označení volajícího jako nativní a vyhněte se zařazování.

Upozornění experimentálního rozhraní API pro WinRT

Rozhraní API WinRT, která jsou vydána pro experimentování a zpětná vazba, jsou vyzdobena Windows.Foundation.Metadata.ExperimentalAttribute. V sadě Visual Studio 2017 verze 15.3 kompilátor vygeneruje pro tento atribut upozornění C4698 . Několik rozhraní API v předchozích verzích sady Windows SDK již bylo upraveno atributem a volání těchto rozhraní API teď aktivují toto upozornění kompilátoru. Novější sady Windows SDK mají atribut odebraný ze všech expedovaných typů. Pokud používáte starší sadu SDK, budete muset tato upozornění potlačit pro všechna volání dodaných typů.

Následující kód vygeneruje upozornění 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

Pokud chcete upozornění zakázat, přidejte #pragma:

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

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

#pragma warning(pop)

Mimořádkovou definici členské funkce šablony

Visual Studio 2017 verze 15.3 způsobí chybu u zastaralé definice členské funkce šablony, která nebyla deklarována ve třídě. Následující kód teď vytvoří chybu C2039:

struct S {};

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

Pokud chcete chybu opravit, přidejte do třídy deklaraci:

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

Pokus o převzetí adresy this ukazatele

V jazyce C++ this je hodnota prvalue ukazatele typu na X. Nemůžete vzít adresu this nebo ji svázat s odkazem lvalue. V předchozích verzích sady Visual Studio by kompilátor umožnil obejít toto omezení použitím přetypování. V sadě Visual Studio 2017 verze 15.3 kompilátor způsobí chybu C2664.

Převod na nepřístupnou základní třídu

Visual Studio 2017 verze 15.3 způsobí chybu při pokusu o převod typu na základní třídu, která je nepřístupná. Následující kód je špatně vytvořený a může potenciálně způsobit chybové ukončení za běhu. Kompilátor teď vytvoří C2243 , když uvidí kód takto:

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

Výchozí argumenty nejsou povoleny pro definice členských funkcí mimo řádek.

Výchozí argumenty nejsou povoleny u zastaralých definic členských funkcí ve třídách šablon. Kompilátor vydá upozornění pod /permissive, a pevně chyba pod /permissive-.

V předchozích verzích sady Visual Studio by následující nevytvořený kód mohl potenciálně způsobit chybové ukončení modulu runtime. Visual Studio 2017 verze 15.3 generuje upozornění 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
{
    // ...
}

Pokud chcete chybu opravit, odeberte = false výchozí argument.

offsetof Použití s návrhem složeného členu

V sadě Visual Studio 2017 verze 15.3 při použití offsetof(T, m) m je "složený člen designator" se při kompilaci s /Wall možností zobrazí upozornění. Následující kód je špatně vytvořený a může potenciálně způsobit chybové ukončení za běhu. Visual Studio 2017 verze 15.3 generuje upozornění 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]);

Pokud chcete kód opravit, zakažte upozornění pomocí direktivy pragma nebo změňte kód tak, aby ho nepoužíval offsetof:

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

Použití offsetof se statickým datovým členem nebo členovou funkcí

V sadě Visual Studio 2017 verze 15.3 se při použití offsetof(T, m) m odkazuje na statického datového člena nebo členské funkce, dojde k chybě. Následující kód způsobí chybu 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'

Tento kód je špatně vytvořený a může potenciálně způsobit chybové ukončení za běhu. Chcete-li chybu opravit, změňte kód tak, aby již nevyvolal nedefinované chování. Jedná se o nepřenosných kód, který není zakázán standardem C++.

Nové upozornění na __declspec atributy

V sadě Visual Studio 2017 verze 15.3 už kompilátor nebude ignorovat atributy, pokud __declspec(...) se použijí před extern "C" specifikací propojení. Dříve by kompilátor ignoroval atribut, který by mohl mít vliv na modul runtime. Když jsou /Wall tyto možnosti /WX nastavené, následující kód vygeneruje upozornění C4768:

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

Pokud chcete upozornění opravit, vložte extern "C" nejprve:

extern "C" __declspec(noinline) HRESULT __stdcall

Toto upozornění je ve výchozím nastavení vypnuté v sadě Visual Studio 2017 verze 15.3 a má vliv pouze na kód zkompilovaný pomocí /Wall /WX. Počínaje sadou Visual Studio 2017 verze 15.5 je ve výchozím nastavení povolená jako upozornění úrovně 3.

decltype a volání odstraněných destruktorů

V předchozích verzích sady Visual Studio kompilátor nerozpoznal, kdy došlo k volání odstraněného destruktoru v kontextu výrazu přidruženého decltypek . V sadě Visual Studio 2017 verze 15.3 způsobí následující kód chybu 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
}

Neinicializované proměnné const

Verze Visual Studio 2017 RTW měla regresi: kompilátor jazyka C++ nedal diagnostiku neinicializované const proměnné. Tato regrese byla opravena v sadě Visual Studio 2017 verze 15.3. Následující kód teď vytvoří upozornění C4132:

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

Chcete-li chybu opravit, přiřaďte hodnotu Value.

Prázdné deklarace

Visual Studio 2017 verze 15.3 teď varuje před prázdnými deklaracemi pro všechny typy, nikoli pouze předdefinované typy. Následující kód nyní vytvoří upozornění úrovně 2 C4091 pro všechny čtyři deklarace:

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

Pokud chcete upozornění odebrat, zakomentujte nebo odeberte prázdné deklarace. V případech, kdy je nepojmenovaný objekt určen k vedlejšímu efektu (například RAII), by měl být uveden název.

Upozornění je vyloučeno /Wv:18 a je ve výchozím nastavení zapnuté na úrovni upozornění W2.

std::is_convertible pro typy polí

Předchozí verze kompilátoru poskytly nesprávné výsledky pro std::is_convertible typy polí. Tento požadovaný zapisovač knihovny pro speciální případ kompilátoru jazyka Microsoft C++ při použití std::is_convertible<...> vlastnosti typu. V následujícím příkladu statické kontrolní výrazy předávají starší verze sady Visual Studio, ale v sadě Visual Studio 2017 verze 15.3 selžou:

#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> se vypočítá tak, že zkontrolujete, jestli je definice imaginární funkce správně vytvořená:

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

Privátní destruktory a std::is_constructible

Předchozí verze kompilátoru ignorovaly, zda byl destruktor při rozhodování o výsledku std::is_constructible. Nyní je považuje za ně. V následujícím příkladu statické kontrolní výrazy předávají starší verze sady Visual Studio, ale v sadě Visual Studio 2017 verze 15.3 selžou:

#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átní destruktory způsobují, že typ není konstruktovatelný. std::is_constructible<T, Args...> se vypočítá tak, jako by byla zapsána následující deklarace:

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

Toto volání znamená volání destruktoru.

C2668: Nejednoznačné rozlišení přetížení

Předchozí verze kompilátoru se někdy nepodařilo rozpoznat nejednoznačnost, když našla více kandidátů pomocí deklarací i vyhledávání závislých na argumentech. Toto selhání může vést k nesprávnému přetížení a neočekávanému chování modulu runtime. V následujícím příkladu visual Studio 2017 verze 15.3 správně vyvolá 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
}

Pokud chcete kód opravit, odeberte příkaz using N::f , pokud jste chtěli volat ::f().

C2660: Deklarace místních funkcí a vyhledávání závislé na argumentech

Deklarace místní funkce skryjí deklaraci funkce v uzavřeném oboru a zakazují vyhledávání závislé na argumentech. Předchozí verze kompilátoru v tomto případě vždy provedly vyhledávání závislé na argumentech. Pokud kompilátor zvolil nesprávné přetížení, mohlo by to potenciálně vést k neočekávanému chování modulu runtime. Tato chyba je obvykle způsobená nesprávným podpisem deklarace místní funkce. V následujícím příkladu visual Studio 2017 verze 15.3 správně vyvolá 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);
}

Pokud chcete tento problém vyřešit, změňte f(S) podpis nebo ho odeberte.

C5038: pořadí inicializace v seznamech inicializátorů

Členové třídy se inicializují v pořadí, v jakém jsou deklarovány, nikoli v pořadí, ve kterém se zobrazují v seznamech inicializátorů. Předchozí verze kompilátoru se neopozorovaly, když se pořadí seznamu inicializátorů liší od pořadí deklarace. Tento problém může vést k nedefinovaným chování modulu runtime, pokud inicializace jednoho člena závisí na jiném členu v seznamu, který se už inicializuje. V následujícím příkladu Visual Studio 2017 verze 15.3 (s /Wall) vyvolá upozornění 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;
};

Pokud chcete tento problém vyřešit, uspořádejte seznam inicializátorů tak, aby měl stejné pořadí jako deklarace. Podobné upozornění se vyvolá, když jeden nebo oba inicializátory odkazují na členy základní třídy.

Toto upozornění je ve výchozím nastavení vypnuté a má vliv pouze na kód zkompilovaný pomocí /Wall.

Vylepšení shody ve verzi 15.5

Funkce označené [14] jsou k dispozici bezpodmínečně i v /std:c++14 režimu.

Nový přepínač kompilátoru pro extern constexpr

V dřívějších verzích sady Visual Studio kompilátor vždy dal proměnnou constexpr interní propojení, i když byla proměnná označena extern. V sadě Visual Studio 2017 verze 15.5 nový přepínač /Zc:externConstexprkompilátoru umožňuje správné chování a chování odpovídající standardům. Další informace najdete v tématu extern constexpr propojení.

Odebrání specifikací dynamických výjimek

P0003R5 specifikace dynamických výjimek byly v jazyce C++11 zastaralé. Funkce se odebere z C++17, ale zastaralá throw() specifikace (stále) se uchovává výhradně jako alias pro noexcept(true). Další informace naleznete v tématu Dynamické specifikace výjimky odebrání a noexcept.

not_fn()

not_fn P0005R4 je nahrazením not1 a not2.

Přemíslání enable_shared_from_this

enable_shared_from_this P0033R1 byla přidána do C++11. Standard C++17 aktualizuje specifikaci, aby lépe zvládla určité rohové případy. [14]

Splicing maps and sets

P0083R3 Tato funkce umožňuje extrakci uzlů z asociativních kontejnerů (tjmap. , set, unordered_map), unordered_setkteré lze potom upravit a vložit zpět do stejného kontejneru nebo do jiného kontejneru, který používá stejný typ uzlu. (Běžným případem použití je extrakce uzlu z std::mapklíče, změna klíče a opětovné vložení.)

Vyřazení částí knihovny zastaralá

P0174R2 Několik funkcí standardní knihovny jazyka C++ bylo v průběhu let nahrazeno novějšími funkcemi, jinak nebyly nalezeny užitečné nebo problematické. Tyto funkce jsou oficiálně zastaralé v jazyce C++17.

Odebrání podpory alokátoru v std::function

P0302R1 Před C++17 měla šablona std::function třídy několik konstruktorů, které vzaly argument alokátoru. Použití alokátorů v tomto kontextu však bylo problematické a sémantika nebyla nejasná. Byly odstraněny problémy s kontraktory.

Opravy pro not_fn()

P0358R1 Nové formulace poskytuje std::not_fn podporu šíření kategorie hodnot při použití při vyvolání obálky.

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

P0414R2 sloučení shared_ptr změn ze základních knihoven na C++17. [14]

Oprava shared_ptr polí

P0497R0 Opravy shared_ptr podpory polí. [14]

Objasňující insert_return_type

P0508R0 Asociativní kontejnery s jedinečnými klíči a neuspořádané kontejnery s jedinečnými klíči mají členovou funkciinsert, která vrací vnořený typ insert_return_type. Tento návratový typ je nyní definován jako specializace typu, který je parametrizován v Iteratoru a NodeType kontejneru.

Vložené proměnné pro standardní knihovnu

Pro P0607R0 je nyní deklarováno několik běžných proměnných deklarovaných ve standardní knihovně.

Zastaralé funkce přílohy D

Příloha D standardu C++ obsahuje všechny funkce, které byly zastaralé, včetně shared_ptr::unique(), <codecvt>a namespace std::tr1. Pokud je nastavena možnost kompilátoru /std:c++17 nebo novější, jsou téměř všechny standardní funkce knihovny v příloze D označené jako zastaralé. Další informace naleznete v tématu Standardní funkce knihovny v příloze D jsou označeny jako zastaralé.

Obor std::tr2::sys názvů v <experimental/filesystem> této chvíli ve výchozím nastavení vygeneruje upozornění /std:c++14 na vyřazení a ve výchozím nastavení se odebere pod /std:c++17 a později.

Lepší shoda <iostream> díky tomu, že se vyhnete nestandardnímu rozšíření (explicitní specializace ve třídě).

Standardní knihovna teď interně používá šablony proměnných.

Standardní knihovna byla aktualizována v reakci na změny kompilátoru C++17. Aktualizace zahrnují přidání noexcept do systému typů a odebrání dynamických specifikací výjimek.

Částečná změna řazení

Kompilátor teď správně odmítne následující kód a zobrazí správnou chybovou zprávu:

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

Problémem v předchozím příkladu je, že existují dva rozdíly v typech (const vs. non-const a pack vs. non-pack). Pokud chcete odstranit chybu kompilátoru, odeberte jeden z rozdílů. Kompilátor pak může jednoznačně uspořádat funkce.

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

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

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

Obslužné rutiny výjimek

Obslužné rutiny odkazu na pole nebo typ funkce se nikdy neshodují s žádným objektem výjimky. Kompilátor nyní správně dodržuje toto pravidlo a vyvolává upozornění na úroveň 4, C4843. Také již neodpovídá obslužné rutině char* nebo wchar_t* řetězcového literálu při /Zc:strictStrings použití.

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

Následující kód se této chybě vyhne:

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

std::tr1 Obor názvů je zastaralý.

Nestandardní std::tr1 obor názvů je nyní označený jako zastaralý v režimech C++14 i C++17. V sadě Visual Studio 2017 verze 15.5 vyvolá následující kód 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.

Pokud chcete chybu opravit, odeberte odkaz na tr1 obor názvů:

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

Standardní funkce knihovny v příloze D jsou označené jako zastaralé

Pokud je nastavený přepínač kompilátoru /std:c++17 nebo režim nebo novější, jsou téměř všechny standardní funkce knihovny v příloze D označené jako zastaralé.

V sadě Visual Studio 2017 verze 15.5 vyvolá následující kód 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.

Pokud chcete chybu opravit, postupujte podle pokynů v textu upozornění, jak je znázorněno v následujícím kódu:

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

Neodkazované místní proměnné

V sadě Visual Studio 15.5 se upozornění C4189 vygeneruje ve více případech, jak je znázorněno v následujícím kódu:

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

Pokud chcete chybu opravit, odeberte nepoužitou proměnnou.

Jednořádkové komentáře

V sadě Visual Studio 2017 verze 15.5 už kompilátor jazyka C nevygeneruje upozornění C4001 a C4179. Dříve byly generovány pouze pod přepínačem kompilátoru /Za . Upozornění už nejsou potřeba, protože jednořádkové komentáře jsou od C99 součástí standardu C.

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

Pokud kód nemusí být zpětně kompatibilní, vyhněte se upozornění odebráním potlačení C4001 a C4179. Pokud kód musí být zpětně kompatibilní, potlačit pouze C4619.

/* C only */

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

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

__declspecatributy s propojením extern "C"

V dřívějších verzích sady Visual Studio kompilátor ignoroval __declspec(...) atributy při __declspec(...) použití před extern "C" specifikací propojení. Toto chování způsobilo vygenerování kódu, který uživatel neměl v úmyslu, a to s možnými důsledky za běhu. Upozornění C4768 bylo přidáno v sadě Visual Studio verze 15.3, ale ve výchozím nastavení bylo vypnuté. V sadě Visual Studio 2017 verze 15.5 je upozornění ve výchozím nastavení povolené.

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

Pokud chcete chybu opravit, umístěte specifikaci propojení před atribut __declspec:

extern "C" __declspec(noinline) HRESULT __stdcall

Toto nové upozornění C4768 je uvedeno v některých hlavičkách sady Windows SDK, které byly dodány se sadou Visual Studio 2017 15.3 nebo starší (například: verze 10.0.15063.0, označovaná také jako RS2 SDK). Byly však opraveny novější verze hlaviček sady Windows SDK (konkrétně ShlObj.h a ShlObj_core.h), aby se upozornění nevytvářelo. Když se zobrazí toto upozornění pocházející z hlaviček sady Windows SDK, můžete provést tyto akce:

  1. Přejděte na nejnovější sadu Windows SDK, která je k dispozici ve verzi sady Visual Studio 2017 verze 15.5.

  2. Vypněte upozornění kolem #include hlavičkového příkazu sady Windows SDK:

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

extern constexpr propojení

V dřívějších verzích sady Visual Studio kompilátor vždy dal proměnnou constexpr interní propojení, i když byla proměnná označena extern. V sadě Visual Studio 2017 verze 15.5 umožňuje nový přepínač kompilátoru (/Zc:externConstexpr) správné chování odpovídající standardům. Nakonec se toto chování stane výchozím.

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

Pokud soubor hlavičky obsahuje deklarovanou extern constexprproměnnou, musí být označen __declspec(selectany) tak, aby se správně zkombinovaly jeho duplicitní deklarace:

extern constexpr __declspec(selectany) int x = 10;

typeid nelze použít u neúplného typu třídy.

V dřívějších verzích sady Visual Studio kompilátor nesprávně povolil následující kód, což vede k potenciálně nesprávným informacím o typu. V sadě Visual Studio 2017 verze 15.5 kompilátor správně vyvolá chybu:

#include <typeinfo>

struct S;

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

std::is_convertible cílový typ

std::is_convertible vyžaduje, aby cílový typ byl platným návratovým typem. V dřívějších verzích sady Visual Studio kompilátor nesprávně povolil abstraktní typy, což může vést k nesprávnému rozlišení přetížení a neočekávanému chování modulu runtime. Následující kód teď správně vyvolá C2338:

#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

Pokud se chcete této chybě vyhnout, měli byste při použití is_convertible porovnat typy ukazatelů, protože porovnání typu bez ukazatele může selhat, pokud je jeden typ abstraktní:

#include <type_traits>

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

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

Odebrání dynamické specifikace výjimek a noexcept

V jazyce C++17 throw() je alias pro noexcepta throw(<type list>) throw(...) jsou odebrány a některé typy mohou zahrnovat noexcept. Tato změna může způsobit problémy s kompatibilitou zdroje s kódem, který odpovídá C++14 nebo staršímu. Přepínač /Zc:noexceptTypes- lze použít k návratu k verzi noexcept C++14 při použití režimu C++17 obecně. Umožňuje aktualizovat zdrojový kód tak, aby odpovídal C++17, aniž byste museli přepisovat veškerý throw() kód najednou.

Kompilátor teď také diagnostikuje neshodnější specifikace výjimek v deklaracích v režimu C++17 nebo s novým upozorněním /permissive- C5043.

Následující kód při použití přepínače vygeneruje C5043 a C5040 v sadě Visual Studio 2017 verze 15.5 /std:c++17 :

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

Pokud chcete odebrat chyby, které stále používáte /std:c++17, přidejte /Zc:noexceptTypes- přepínač na příkazový řádek nebo jinak aktualizujte kód tak, aby se používal noexcept, jak je znázorněno v následujícím příkladu:

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

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

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

Vložené proměnné

Statické constexpr datové členy jsou nyní implicitně inline, což znamená, že jejich deklarace v rámci třídy je nyní jejich definice. Použití zastaralé definice datového členu static constexpr je redundantní a nyní zastaralé. Když se v sadě Visual Studio 2017 verze 15.5 použije následující kód, /std:c++17 zobrazí se upozornění 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(...) Upozornění C4768 je teď ve výchozím nastavení zapnuté

Upozornění bylo přidáno v sadě Visual Studio 2017 verze 15.3, ale ve výchozím nastavení bylo vypnuté. V sadě Visual Studio 2017 verze 15.5 je ve výchozím nastavení upozornění zapnuté. Další informace naleznete v tématu Nové upozornění na __declspec atributy.

Výchozí funkce a __declspec(nothrow)

Kompilátor dříve povolil, aby výchozí funkce byly deklarovány, __declspec(nothrow) pokud odpovídající základní/členské funkce povolily výjimky. Toto chování je v rozporu se standardem C++ a může způsobit nedefinované chování za běhu. Standard vyžaduje, aby se takové funkce definovaly jako odstraněné, pokud dojde k neshodě specifikací výjimek. V části /std:c++17následující kód vyvolá C2280:

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

Chcete-li tento kód opravit, odeberte __declspec(nothrow) z výchozí funkce, nebo odeberte a zadejte = default definici funkce spolu s požadovaným zpracováním výjimek:

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 a částečné specializace

V noexcept systému typů nemusí být částečné specializace pro odpovídající konkrétní typy "volatelné" zkompilovány nebo selžou volbu primární šablony, protože chybí částečná specializace pro ukazatele-na-noexcept-functions.

V takových případech možná budete muset přidat další částečné specializace, abyste zvládli noexcept ukazatele funkce a noexcept ukazatele na členské funkce. Tato přetížení jsou pouze v /std:c++17 režimu nebo novějším. Pokud je potřeba zachovat zpětnou kompatibilitu s C++14 a píšete kód, který ostatní využívají, měli byste tyto nové přetížení chránit uvnitř #ifdef direktiv. Pokud pracujete v samostatném modulu, můžete místo použití #ifdef stráží jednoduše kompilovat pomocí /Zc:noexceptTypes- přepínače.

Následující kód se zkompiluje, /std:c++14 ale selže /std:c++17 pod chybou 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>'
}

Následující kód bude /std:c++17 úspěšný, protože kompilátor zvolí novou částečnou specializaci A<void (*)() noexcept>:

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
}

Vylepšení shody ve verzi 15.6

Základy knihovny C++17 V1

P0220R1 zahrnuje do standardu technické specifikace Knihovny Fundamentals pro C++17. Zahrnuje aktualizace , <experimental/tuple><experimental/optional>, , <experimental/functional><experimental/any>, <experimental/string_view>, <experimental/memory>, , <experimental/memory_resource>, a <experimental/algorithm>.

C++17: Vylepšení odpočtu argumentů šablony třídy pro standardní knihovnu

P0739R0 Přesunutí adopt_lock_t na začátek seznamu parametrů, scoped_lock aby bylo možné používat konzistentní použití scoped_lock. Umožňuje std::variant konstruktoru účastnit se řešení přetížení v dalších případech, aby bylo možné povolit přiřazení kopírování.

Vylepšení shody ve verzi 15.7

C++17: Rewording inheriting constructors

P0136R1 určuje, že deklarace, using která pojmenuje konstruktor, nyní zviditelní odpovídající konstruktory základní třídy inicializace odvozené třídy, nikoli deklarování více odvozených konstruktorů třídy. Toto přeformulování je změna z C++14. V sadě Visual Studio 2017 verze 15.7 a novějším v /std:c++17 režimu a novějším může být kód platný v jazyce C++14 a použití dědění konstruktorů nemusí být platné nebo může mít jinou sémantiku.

Následující příklad ukazuje chování 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).

Následující příklad ukazuje /std:c++17 chování v sadě 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)

Další informace naleznete v tématu Konstruktory.

C++17: Rozšířená inicializace agregace

P0017R1

Pokud je konstruktor základní třídy neveřejný, ale přístupný odvozené třídě, pak /std:c++17 v režimu a novějším v sadě Visual Studio 2017 verze 15.7 již nelze použít prázdné složené závorky k inicializaci objektu odvozeného typu. Následující příklad ukazuje chování odpovídající 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.

V jazyce C++17 Derived se nyní považuje za agregační typ. To znamená, že inicializace Base prostřednictvím privátního výchozího konstruktoru probíhá přímo v rámci rozšířeného pravidla inicializace agregace. Base Dříve byl privátní konstruktor volána prostřednictvím konstruktoru Derived a byl úspěšný z důvodu deklarace přítele. Následující příklad ukazuje chování C++17 v sadě Visual Studio verze 15.7 v /std:c++17 režimu:

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: Deklarování parametrů šablony bez typu pomocí automatického

P0127R2

V /std:c++17 režimu může kompilátor nyní odvodit typ netypového argumentu šablony, který je deklarován pomocí 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

Jedním z účinků této nové funkce je, že platný kód C++14 nemusí být platný nebo může mít jinou sémantiku. Například některá přetížení, která byla dříve neplatná, jsou nyní platná. Následující příklad ukazuje kód C++14, který se zkompiluje, protože volání example(p) je vázáno na example(void*);. V sadě Visual Studio 2017 verze 15.7 je v /std:c++17 režimu example nejvhodnější šablona funkce.

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
}

Následující příklad ukazuje kód C++17 v sadě Visual Studio 15.7 v /std:c++17 režimu:

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: Převody základních řetězců (částečné)

P0067R5 funkce nezávislé na národním prostředí pro převody mezi celými čísly a řetězci a mezi čísly s plovoucí desetinnou čárkou a řetězci.

C++20: Vyhněte se zbytečnému rozpadu (částečnému)

P0777R1 přidává rozdíl mezi konceptem "rozpadu" a tím, že jednoduše odstraňuje kvalifikátory const nebo reference. Nové vlastnosti remove_reference_t typu se decay_t v některých kontextech nahradí. Podpora pro remove_cvref_t je implementována v sadě Visual Studio 2019.

C++17: Paralelní algoritmy

P0024R2 TS paralelismu je začleněn do standardu s drobnými úpravami.

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

P0030R1 přidá tři std::hypotnová přetížení pro typy float, doublea long doublekaždý z nich má tři vstupní parametry.

C++17: <filesystem>

P0218R1 přijme TS systému souborů do standardu s několika úpravami formulace.

C++17: Matematické speciální funkce

P0226R1 přijímá předchozí technické specifikace matematických speciálních funkcí do standardní <cmath> hlavičky.

C++17: Průvodci odpočtem pro standardní knihovnu

P0433R2 aktualizace STL, aby využívaly výhod přijetí P0091R3 C++17, což přidává podporu pro dedukci argumentů šablony třídy.

C++17: Oprava převodů základních řetězců

P0682R1 Přesuňte nové funkce převodu základních řetězců z P0067R5 do nové hlavičky <charconv> a proveďte další vylepšení, včetně změny zpracování chyb tak, aby se místo toho používaly std::errc std::error_code.

C++17: constexpr pro char_traits (částečné)

P0426R1 Změny std::traits_type členských funkcí length, comparea find provádět std::string_view použitelné v konstantních výrazech. (V sadě Visual Studio 2017 verze 15.6 se podporuje jenom pro Clang/LLVM. Ve verzi 15.7 je podpora téměř dokončena i pro ClXX.)

C++17: Výchozí argument v šabloně primární třídy

Tato změna chování je předpokladem pro P0091R3 - Template argument deduction for class templates.

Dříve kompilátor ignoroval výchozí argument v šabloně primární třídy:

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

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

V /std:c++17 režimu v sadě Visual Studio 2017 verze 15.7 není výchozí argument ignorován:

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

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

Závislý překlad názvů

Tato změna chování je předpokladem pro P0091R3 - Template argument deduction for class templates.

V následujícím příkladu se kompilátor v sadě Visual Studio 15.6 a starší přeloží D::type na B<T>::type šablonu primární třídy.

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

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

Visual Studio 2017 verze 15.7 v /std:c++17 režimu vyžaduje typename klíčové slovo v using příkazu v D. Bez typename, kompilátor vyvolá upozornění C4346: 'B<T*>::type': dependent name is not a type a chyba 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]] atribut – zvýšení úrovně upozornění

V sadě Visual Studio 2017 verze 15.7 v /std:c++17 režimu se úroveň upozornění C4834 zvýší z W3 na W1. Upozornění můžete zakázat přetypováním na void, nebo předáním /wd:4834 kompilátoru.

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

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

Seznam inicializace základní třídy konstruktoru šablony Variadic

V předchozích edicích sady Visual Studio byl nesprávně povolen seznam inicializace základní třídy konstruktoru šablony variadic, který chyběl. V sadě Visual Studio 2017 verze 15.7 se vyvolá chyba kompilátoru.

Následující příklad kódu v sadě Visual Studio 2017 verze 15.7 vyvolá chybu 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;

Chcete-li chybu opravit, změňte výraz na B() B<T>().

constexpr agregace inicializace

Předchozí verze kompilátoru C++ nesprávně zpracovávaly agregační constexpr inicializaci. Kompilátor přijal neplatný kód, ve kterém měl seznam aggregate-init-list příliš mnoho prvků a vytvořil pro něj chybný kód objektu. Následující kód je příkladem takového kódu:

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

V sadě Visual Studio 2017 verze 15.7 update 3 a novější teď předchozí příklad vyvolává C2078. Následující příklad ukazuje, jak opravit kód. Při inicializaci std::array s vnořenými složenými složenými závorkami dejte vnitřní matici vlastní složený seznam:

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

Vylepšení shody ve verzi 15.8

typename u nekvalifikovaných identifikátorů

V /permissive- režimu už kompilátor nepřijímá klíčová typename slova u nekvalifikovaných identifikátorů v definicích šablon aliasů. Následující kód teď vytvoří C7511:

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

Pokud chcete chybu opravit, změňte druhý řádek na using X = T;.

__declspec() na pravé straně definic šablon aliasů

__declspec na pravé straně definice šablony aliasu už není povolená. Dříve kompilátor přijal tento kód, ale ignoroval ho. Při použití aliasu by se nikdy nezpůsobilo upozornění na vyřazení.

Místo toho lze použít standardní atribut [[deprecated]] C++ a je respektován v sadě Visual Studio 2017 verze 15.6. Následující kód teď vytvoří C2760:

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

Chybu opravíte tak, že změníte kód na následující (s atributem umístěným před definicí aliasu =):

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

Diagnostika vyhledávání dvoufázových názvů

Dvoufázové vyhledávání názvů vyžaduje, aby pro šablonu v okamžiku definice byly viditelné jiné než závislé názvy použité v těle šablony. Dříve kompilátor jazyka Microsoft C++ ponechal nenalezený název tak, jak se nehleděl, dokud nedojde k vytvoření instance. Teď vyžaduje, aby názvy, které nejsou závislé, byly v textu šablony svázané.

Jedním ze způsobů, jak se to dá manifestovat, je vyhledávání do závislých základních tříd. Dříve kompilátor povolil použití názvů definovaných v závislých základních třídách. Je to proto, že by se hledali během vytváření instancí, když jsou vyřešeny všechny typy. Teď je kód považován za chybu. V těchto případech můžete proměnnou vynutit, aby se vyhledala v době vytvoření instance, a to tak, že ji opravíte pomocí typu základní třídy nebo jinak nastavíte, že bude závislá, například přidáním this-> ukazatele.

V /permissive- režimu teď následující kód vyvolá 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
    }
};

Chcete-li chybu opravit, změňte return příkaz na return this->base_value;.

Poznámka:

Ve verzích knihovny Boost.Python před verzí 1.70 existuje alternativní řešení specifické pro šablonu pro deklaraci předání šablony v unwind_type.hppsouboru . V /permissive- režimu začínajícím v sadě Visual Studio 2017 verze 15.8 (_MSC_VER==1915) kompilátor MSVC správně vyhledá název závislý na argumentech (ADL). Teď je konzistentní s jinými kompilátory, takže toto alternativní řešení hlídá nepotřebné. Abyste se vyhnuli chybě C3861: 'unwind_type': identifier not foundaktualizujte knihovnu Boost.Python.

předávání deklarací a definic v oboru názvů std

Standard C++ neumožňuje uživateli přidávat do oboru názvů stdpředávací deklarace nebo definice . Přidání deklarací nebo definic do oboru názvů std nebo do oboru názvů v rámci oboru názvů std teď vede k nedefinovanýmu chování.

V budoucnu Microsoft přesune umístění, kde jsou definovány některé standardní typy knihoven. Tato změna přeruší existující kód, který přidá deklarace předávání do oboru názvů std. Nové upozornění C4643 pomáhá identifikovat takové zdrojové problémy. Upozornění je povolené v /default režimu a je ve výchozím nastavení vypnuté. Bude mít vliv na programy, které jsou kompilovány s /Wall nebo /WX.

Následující kód teď vyvolá C4643:

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

Chcete-li chybu opravit, použijte direktivu #include místo předávané deklarace:

#include <vector>

Konstruktory, kteří sami delegují

Standard jazyka C++ naznačuje, že kompilátor by měl vygenerovat diagnostiku, když delegující konstruktor deleguje na sebe. Kompilátor Jazyka C++ v /std:c++17 jazyce Microsoft C++ a /std:c++latest režimy nyní vyvolává C7535.

Bez této chyby se následující program zkompiluje, ale vygeneruje nekonečnou smyčku:

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

Chcete-li se vyhnout nekonečné smyčce, delegujte na jiný konstruktor:

class X {
public:

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

offsetof s konstantními výrazy

offsetof byla tradičně implementována pomocí makra, které vyžaduje reinterpret_cast. Toto použití není v kontextech, které vyžadují konstantní výraz, ale kompilátor jazyka Microsoft C++ jej tradičně povolil. Makro offsetof , které je odesláno jako součást standardní knihovny správně používá vnitřní kompilátor (__builtin_offsetof), ale mnoho lidí použilo trik makra k definování vlastní offsetof.

V sadě Visual Studio 2017 verze 15.8 kompilátor omezuje oblasti, které se můžou tyto reinterpret_cast operátory objevit ve výchozím režimu, aby kód odpovídal standardnímu chování jazyka C++. V rámci /permissive-, omezení jsou ještě přísnější. Použití výsledku offsetof na místech, které vyžadují konstantní výrazy, může vést k kódu, který vydává upozornění C4644 nebo C2975.

Následující kód vyvolá C4644 ve výchozím nastavení a /std:c++17 režimech a C2975 v /permissive- režimu:

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

Pokud chcete chybu opravit, použijte offsetof ji tak, jak je definováno:<cstddef>

#include <cstddef>

struct Data {
    int x;
};

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

Cv-kvalifikátory základních tříd, na které se vztahuje rozšíření balíčku

Předchozí verze kompilátoru jazyka Microsoft C++ nezjistily, že základní třída měla kvalifikátory cv, pokud byla také předmětem rozšíření balíčku.

V sadě Visual Studio 2017 verze 15.8 v /permissive- režimu vyvolá následující kód 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 klíčové slovo a vnořené specifikátory názvů

V /permissive- režimu teď kompilátor vyžaduje template , aby klíčové slovo předchází názvu šablony, pokud přichází po závislém specifikátoru vnořeného názvu.

Následující kód v /permissive- režimu teď vyvolává C7510:

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

Pokud chcete chybu opravit, přidejte template do Base<T>::example<int>(); příkazu klíčové slovo, jak je znázorněno v následujícím příkladu:

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

Vylepšení shody ve verzi 15.9

Pořadí vyhodnocení zleva doprava pro operátory ->*, [], >>, a <<

Počínaje jazykem C++17 musí být operandy operátorů ->*, [], >>a << musí být vyhodnoceny v pořadí zleva doprava. Existují dva případy, kdy kompilátor nemůže zaručit toto pořadí:

  • pokud jeden z výrazů operandu je objekt předaný hodnotou nebo obsahuje objekt předaný hodnotou, nebo

  • při kompilaci pomocí a /clrjeden z operandů je pole objektu nebo prvku pole.

Kompilátor vygeneruje upozornění C4866, pokud nemůže zaručit vyhodnocení zleva doprava. Kompilátor vygeneruje toto upozornění pouze v případě, že /std:c++17 je zadána nebo novější, protože požadavek na pořadí zleva doprava těchto operátorů byl zaveden v jazyce C++17.

Chcete-li toto upozornění vyřešit, nejprve zvažte, zda je nezbytné vyhodnocení operandů zleva doprava. Například může být nutné, když vyhodnocení operandů může vést k vedlejším účinkům závislým na pořadí. Pořadí, ve kterém jsou operandy vyhodnoceny, nemá v mnoha případech žádný pozorovatelný účinek. Pokud musí být pořadí vyhodnocení zleva doprava, zvažte, jestli místo toho můžete operandy předat pomocí odkazu const. Tato změna eliminuje upozornění v následující ukázce kódu:

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

Identifikátory v šablonách aliasů členů

Před použitím musí být deklarován identifikátor použitý v definici šablony aliasu člena.

V předchozích verzích kompilátoru byl povolen následující kód. V sadě Visual Studio 2017 verze 15.9 /permissive- kompilátor vyvolá C3861:

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;

Chcete-li chybu opravit, deklarujte from_template před from_template_t.

Změny modulů

V sadě Visual Studio 2017 verze 15.9 kompilátor zvýší C5050 vždy, když možnosti příkazového řádku pro moduly nejsou konzistentní mezi vytvořením modulu a stranami spotřeby modulů. V následujícím příkladu existují dva problémy:

  • Na straně spotřeby (main.cpp) není tato možnost /EHsc zadána.

  • Verze jazyka C++ je /std:c++17 na straně vytvoření a /std:c++14 na straně spotřeby.

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

Kompilátor vyvolá C5050 pro oba tyto případy:

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

Kompilátor také vyvolá C7536 při .ifc manipulaci se souborem. Hlavička rozhraní modulu obsahuje hodnotu hash SHA2 obsahu pod ním. Při importu .ifc se soubor zatřiďuje a pak zkontroluje hodnotu hash zadanou v hlavičce. Pokud se neshodují, vyvolá se chyba C7536:

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

Částečné řazení zahrnující aliasy a nededukované kontexty

Implementace se rozbíhají v částečných pravidlech řazení zahrnujících aliasy v nededukovaných kontextech. V následujícím příkladu vyvolá GCC a kompilátor Microsoft C++ (v /permissive- režimu) chybu, zatímco Clang přijímá kód.

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

Předchozí příklad vyvolá 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>)'

Rozdíly implementace jsou způsobeny regresí ve standardním znění jazyka C++. Řešení problému jádra 2235 odebralo nějaký text, který by umožnil řazení těchto přetížení. Aktuální standard C++ neposkytuje mechanismus pro částečné řazení těchto funkcí, takže jsou považovány za nejednoznačné.

Jako alternativní řešení doporučujeme, abyste se při řešení tohoto problému nespoléhali na částečné řazení. Místo toho použijte SFINAE k odebrání konkrétních přetížení. V následujícím příkladu použijeme pomocnou třídu IsA k odebrání prvního přetížení, pokud Alloc je specializace 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;
}

Neplatné výrazy a nes literály v definicích funkce šablony

Neplatné výrazy a neslovné typy jsou nyní správně diagnostikovány v definicích šablonovaných funkcí, které jsou explicitně specializované. Dříve se tyto chyby pro definici funkce nevygenerovaly. Neplatný výraz nebo nes literálový typ by se však stále diagnostikoval, pokud by byl vyhodnocen jako součást konstantního výrazu.

V předchozích verzích sady Visual Studio se následující kód zkompiluje bez upozornění:

void g();

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

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

V sadě Visual Studio 2017 verze 15.9 vyvolá kód chybu 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'.

Chcete-li se této chybě vyhnout, odeberte constexpr kvalifikátor z explicitní instance funkce f().

Viz také

Shoda jazyka Microsoft C/C++