Share via


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

Microsoft C/C++ v sadě Visual Studio (MSVC) provádí vylepšení shody a opravy chyb v každé verzi. Tento článek uvádí vylepšení podle hlavní verze a pak podle verze. Pokud chcete přejít přímo na změny pro konkrétní verzi, 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í dodržování předpisů jazyka C++ v sadě Visual Studio 2019. Úplný seznam předchozích vylepšení dodržování předpisů najdete v tématu Visual C++ Co je nového 2003 až 2015.

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

S podporou generalizované constexpr a nestatické 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átoru však stále chybí několik funkcí ze standardů C++11 a C++98. Aktuální stav kompilátoru najdete v tématu Shoda jazyka Microsoft C/C++ .

C++11: Podpora výrazů SFINAE ve více knihovnách

Kompilátor dál vylepšuje podporu výrazu SFINAE. Vyžaduje se pro dedukci argumentů šablony a nahrazení, kde decltype výrazy a constexpr se 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 můžou 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 teď můžou 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 . Již není nutné, aby constexpr statická členská funkce byla implicitně const. Další informace najdete v tématu Uvolnění omezení constexpr funkcí.

C++17: Terse static_assert

parametr message pro static_assert je volitelný. Další informace najdete v tématu N3928: Rozšíření static_assert, v2.

C++17: [[fallthrough]] atribut

V /std:c++17 režimu a v pozdějších [[fallthrough]] verzích lze atribut použít v kontextu příkazů switch jako nápovědu kompilátoru, že je zamýšleno výchozí chování. Tento atribut zabraňuje kompilátoru v takových případech vystavovat upozornění. Další informace naleznete v tématu P0188R0 - Wording for [[fallthrough]] attribute.

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

Smyčky založené na for rozsahu už to nevyžadují begin() a end() vrací objekty stejného typu. Tato změna umožňuje end() vrátit sentinel používaný oblastmi v range-v3 a technickou specifikací dokončených, ale ne zcela publikovaných rozsahů. Další informace naleznete v tématu P0184R0 - Generalizing the Range-Based for Loop.

Copy-list-initialization

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 vést k chybám nebo nedefinovaným chováním modulu runtime. Podle , N4594 13.3.1.7p1v copy-list-initialization, kompilátor musí zvážit explicitní konstruktor pro řešení přetížení. Pokud je ale toto konkrétní přetížení zvoleno, 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ě zacházel s inicializací copy-list-initialization stejným způsobem jako běžná inicializace kopírování: zvažoval pouze převod konstruktorů pro řešení přetížení. V následujícím příkladu 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 se podobá předchozímu příkladu, ale vyvolává 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é typedefs

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

Pokud chcete chybu opravit, buď 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 předávané do variadické funkce, jako printf je například, triviálně kopírovatelné. Když jsou takové objekty předány, kompilátor jednoduše provede bitové kopírování a nevolá konstruktor nebo 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
}

Pokud chcete 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 použijte statické přetypování k převodu objektu před jeho předáním:

    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() parametr měl použít k přetypování objektu CString 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 v konstrukci tříd

V sadě Visual Studio 2015 kompilátor někdy nesprávně ignoruje cv-kvalifikátor 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 vyvolá chybu kompilátoru v sadě Visual Studio 2017:

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

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

Pokud chcete chybu opravit, deklarujte operator int() ji jako const.

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

Předchozí verze kompilátoru nekontrolují přístup ke kvalifikovaným názvům v některých kontextech šablon. 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 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 došlo k chybě kompilátoru. Konkrétní chyba se může lišit, ale typickou chybou je C2672 , "nenašla se žádná odpovídající přetížená funkce". Následující kód se zkompiluje v sadě Visual Studio 2015, ale vyvolá chybu v sadě 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.
}

Chybějící seznamy argumentů šablony

V sadě Visual Studio 2015 a starších verzích kompilátor nediagnostikoval všechny chybějící seznamy argumentů šablony. Nepovšimla si, kdy se chybějící šablona objevila v seznamu parametrů šablony: například když chybí část výchozího argumentu šablony nebo parametr šablony, který není 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í za běhu. 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

Za účelem podpory expression-SFINAE teď kompilátor analyzuje argumenty decltype , když jsou šablony deklarovány místo vytvoření instance. Pokud je tedy v argumentu decltype nalezena nezávisená specializace, není odložena na dobu vytvoření instance. Zpracuje 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á v anonymním oboru názvů interní propojení, což znamená, že ji nelze 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ě. Následující příklad v sadě Visual Studio 2017 vyvolá chybu 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ších verzích 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ších verzích kompilátor v některých případech nesprávně identifikoval výchozí vlastnost jako výchozí indexer. Problém bylo možné vyřešit pomocí identifikátoru default pro přístup k vlastnosti. Samotné alternativní řešení začalo být problematické poté default , co bylo zavedeno jako klíčové slovo v C++11. 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 pro třídu.

//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 Value podle jejich názvu:

#using "class1.dll"

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

Vylepšení shody ve 15.3

constexpr lambda

Výrazy lambda se teď dají používat v konstantních výrazech. 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 pro povolení větvení v době kompilace. Další informace najdete v tématu if constexpr příkazy.

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

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

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

Nový atribut [[maybe_unused]] ztlumí upozornění, když se entita nepoužívá. Atribut [[nodiscard]] vytvoří upozornění, pokud je vrácená hodnota volání funkce zahozena. Další informace najdete 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 najdete v tématu Atributy v jazyce C++.

Strukturované vazby

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

Pravidla konstrukce pro enum class hodnoty

Pro výčty s vymezeným oborem teď existuje implicitní převod, který není zúžený. Převede 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 pokud zdroj používá syntaxi inicializace seznamu. Další informace najdete v tématech P0138R2 - Construction Rules for enum class Values a Výčty.

Zachytávání *this podle hodnoty

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

Odebírá se operator++ pro bool

operator++ U typů se už nepodporuje bool . Další informace naleznete v tématu P0002R1 - Remove Deprecated operator++(bool).

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

Klíčové register slovo, které bylo dříve zastaralé (a kompilátor ho ignoroval), je teď z jazyka odebráno. Další informace naleznete v tématu P0001R1 - Remove Deprecated Use of the register Keyword.

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

V předchozích verzích sady Visual Studio se kompilátoru v některých případech nepodařilo vygenerovat chybu pro špatně formátovaná volání odstraněné šablony člena. 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
}

Pokud chcete chybu opravit, deklarujte i ji jako int.

Kontroly předběžných podmínek pro vlastnosti typu

Visual Studio 2017 verze 15.3 zlepšuje kontroly předběžných podmínek pro vlastnosti typu tak, aby přísněji dodržovaly standard. Jeden takový šek je pro přiřaditelný. 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řazování za běhu při nativním zařazování na spravované

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

Nyní kompilátor vygeneruje upozornění, pokud najde tuto chybu v době kompilace: nativní objekt s odstraněným objektem kopírování ctor se předává mezi nativní a spravovanou hranicí podle hodnoty. V případech, kdy kompilátor v době kompilace neví, vloží kontrolu za běhu, aby program okamžitě volal std::terminate , když dojde k zařazování špatného formátu. V sadě Visual Studio 2017 verze 15.3 následující kód vyvolá 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 , aby se volající označil jako nativní a zabránilo zařazování.

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

Rozhraní WINRT API, která jsou vydaná pro experimentování a zpětnou vazbu, jsou zdobená pomocí Windows.Foundation.Metadata.ExperimentalAttribute. V sadě Visual Studio 2017 verze 15.3 kompilátor generuje upozornění C4698 pro tento atribut. Několik rozhraní API v předchozích verzích sady Windows SDK už bylo upraveno atributem a volání těchto rozhraní API teď toto upozornění kompilátoru aktivují. Novější sady Windows SDK mají atribut odebraný ze všech dodávaných typů. Pokud používáte starší sadu SDK, budete muset tato upozornění potlačit pro všechna volání odeslaných typů.

Následující kód vyvolá 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řádková definice členské funkce šablony

Visual Studio 2017 verze 15.3 vytvoří chybu pro mimořádkovou definici členské funkce šablony, která nebyla deklarována ve třídě . Následující kód nyní způsobí 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 typu ukazatel na X. Nemůžete vzít adresu this nebo ji svázat s odkazem lvalue. V předchozích verzích sady Visual Studio vám kompilátor umožnil obejít toto omezení pomocí 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 podobný tomuto:

#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 u mimořádkových definic členských funkcí.

Výchozí argumenty nejsou povoleny pro mimořádkové definice členských funkcí v třídách šablon. Kompilátor vydá upozornění v části /permissivea pevnou chybu v části /permissive-.

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

Použití s offsetof označením složeného členu

V sadě Visual Studio 2017 verze 15.3 použití offsetof(T, m)m je " označení složeného členu" výsledkem upozornění při kompilaci s /Wall možností . 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 zobrazí 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, buď zakažte upozornění pomocí direktivy pragma, nebo změňte kód tak, aby 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 člennou funkcí

V sadě Visual Studio 2017 verze 15.3 způsobí použití funkce offsetof(T, m) kde m odkazující na statický datový člen nebo členovou funkci chybu. 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. Pokud chcete chybu opravit, změňte kód tak, aby už nevyvolával nedefinované chování. Jedná se o nepřenosný kód, který je zakázán standardem C++.

Nové upozornění na __declspec atributy

V sadě Visual Studio 2017 verze 15.3 kompilátor už neignoruje atributy, pokud __declspec(...) se použije před extern "C" specifikací propojení. Dříve kompilátor atribut ignoroval, což by mohlo mít vliv na běh. /Wall Při nastavení možností a /WX následující kód vyvolá upozornění C4768:

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

Upozornění opravíte tak, že jako první zadáte extern "C" :

extern "C" __declspec(noinline) HRESULT __stdcall

Toto upozornění je ve výchozím nastavení v sadě Visual Studio 2017 verze 15.3 vypnuté a týká se pouze kódu zkompilovaného pomocí /Wall/WX. Od sady Visual Studio 2017 verze 15.5 je ve výchozím nastavení povolená jako upozornění úrovně 3.

decltype a volá odstraněné destruktory

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

Vydání sady Visual Studio 2017 RTW mělo regresi: kompilátor jazyka C++ nevydal 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

Pokud chcete 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, ne jenom předdefinované typy. Následující kód teď 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 má mít nepojmenovaný objekt vedlejší účinek (například RAII), by měl být pojmenován.

Upozornění je vyloučeno v části /Wv:18 a je ve výchozím nastavení zapnuté pod úrovní 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 zapisovač knihovny vyžaduje zvláštní případ kompilátoru Jazyka Microsoft C++ při použití std::is_convertible<...> vlastnosti typu. V následujícím příkladu statické 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ě zformovaná:

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

Soukromé destruktory a std::is_constructible

Předchozí verze kompilátoru při rozhodování o výsledku std::is_constructibleignorovaly, jestli je destruktor soukromý. Teď je bere v úvahu. V následujícím příkladu statické 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);

Soukromé destruktory způsobují, že typ je nekonstruovatelný. std::is_constructible<T, Args...> se vypočítá, jako by byla zapsána následující deklarace:

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

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

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

V předchozích verzích kompilátoru se někdy nepodařilo rozpoznat nejednoznačnost, když se zjistilo více kandidátů pomocí deklarací a vyhledávání závislého na argumentech. Toto selhání může vést k nesprávnému výběru přetížení a neočekávanému chování za běhu. V následujícím příkladu Visual Studio 2017 verze 15.3 správně vyvolává 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 chcete volat ::f().

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

Deklarace místní funkce skryjí deklaraci funkce v ohraničujícím oboru a zakážou vyhledávání závislé na argumentu. Předchozí verze kompilátoru v tomto případě vždy provedly vyhledávání závislé na argumentu. Pokud kompilátor zvolí nesprávné přetížení, mohlo by to potenciálně vést k neočekávanému chování za běhu. K chybě obvykle dochází kvůli nesprávnému podpisu 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 podpis nebo ho f(S) odeberte.

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

Členy třídy se inicializují v pořadí, v jakém jsou deklarovány, nikoli v pořadí, v jakém se zobrazují v seznamech inicializátorů. Předchozí verze kompilátoru neupozorňovaly, když se pořadí seznamu inicializátorů lišilo od pořadí deklarace. Tento problém může vést k neurčenému chování modulu runtime, pokud inicializace jednoho člena závisela na jiném členovi v seznamu, který je již inicializován. V následujícím příkladu zobrazí Visual Studio 2017 verze 15.3 (s /Wall) 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í je vyvolána, když jeden nebo oba inicializátory odkazují na členy základní třídy.

Toto upozornění je ve výchozím nastavení vypnuté a týká se pouze kódu zkompilovaného pomocí /Wall.

Vylepšení shody ve 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í, a to i v případě, že proměnná byla označena .extern Nový přepínač /Zc:externConstexprkompilátoru v sadě Visual Studio 2017 verze 15.5 umožňuje správné 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 C++11 zastaralé. Funkce je z C++17 odebrána, ale (stále) zastaralá throw() specifikace se uchovává výhradně jako alias pro noexcept(true). Další informace najdete v tématech Odebrání specifikace dynamických výjimek a noexcept.

not_fn()

P0005R4not_fn je nahrazením not1 a not2.

Přepisování slov enable_shared_from_this

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

Spojování map a sad

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

Vyřazování částí zastaralá knihovna

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

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

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

Opravy pro not_fn()

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

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

P0414R2shared_ptr Sloučení změn ze základů knihovny do C++17 [14]

Oprava shared_ptr polí

P0497R0 Opravy shared_ptr podpory polí. [14]

Vyjasnění insert_return_type

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

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

Pro P0607R0 je teď několik běžných proměnných deklarovaných ve standardní knihovně deklarováno jako vložené.

Funkce přílohy D jsou zastaralé

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

Obor std::tr2::sys názvů v <experimental/filesystem> systému teď ve výchozím nastavení vygeneruje upozornění /std:c++14 na vyřazení a ve výchozím nastavení se odebere./std:c++17

Lepší shoda v <iostream> systému 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 zahrnout přidání noexcept do systému typů a odebrání dynamic-exception-specifications.

Částečná změna pořadí

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 ve výše uvedené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 typ matice nebo funkce se nikdy neshodují pro žádný objekt výjimky. Kompilátor teď toto pravidlo správně dodržuje a vyvolá upozornění úrovně 4 , C4843. Při použití také neodpovídá obslužné rutině řetězcového literálu char*/Zc:strictStrings nebo wchar_t* .

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

std::tr1 Nestandardní obor názvů je nyní v režimu C++14 i C++17 označen jako zastaralý. 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í prvky knihovny v příloze D jsou označeny jako zastaralé.

Při nastavení režimu nebo pozdějšího /std:c++17 přepínače kompilátoru jsou téměř všechny standardní prvky knihovny v příloze D označeny 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 vysílá 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žívané proměnné.

Jednořádkové komentáře

V sadě Visual Studio 2017 verze 15.5 už kompilátor jazyka C nevysílají upozornění C4001 a C4179. Dříve se vygenerovaly pouze v rámci přepínače kompilátoru /Za . Upozornění již nejsou potřeba, protože jednořádkové komentáře jsou součástí normy C již od 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'

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, pokud __declspec(...) byly použity před extern "C" specifikací propojení. Toto chování způsobilo vygenerování kódu, který uživatel neměl v úmyslu, s možnými důsledky za běhu. Upozornění C4768 bylo přidáno do sady 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 se zobrazí u některých hlaviček 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). Novější verze hlaviček sady Windows SDK (konkrétně ShlObj.h a ShlObj_core.h) však byly opraveny tak, aby upozornění nevyvolávali. Když se zobrazí toto upozornění z hlaviček sady Windows SDK, můžete provést tyto akce:

  1. Přejděte na nejnovější sadu Windows SDK, která byla dodaná s vydáním sady Visual Studio 2017 verze 15.5.

  2. Vypněte upozornění týkající se #include příkazu hlavičky 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 poskytoval constexpr interní propojení proměnné, i když byla proměnná označena jako extern. V sadě Visual Studio 2017 verze 15.5 nový přepínač kompilátoru (/Zc:externConstexpr) umožňuje správné chování odpovídající standardům. Toto chování se nakonec stane výchozím.

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

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

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ž vedlo 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 typ cíle

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ě povoloval 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í bez typu 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 C++17 throw() je alias pro noexcept, throw(<type list>) a 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ším verzím. Přepínač /Zc:noexceptTypes- se dá použít k návratu k verzi C++14 při noexcept obecném použití režimu C++17. Umožňuje aktualizovat zdrojový kód tak, aby vyhovoval C++17, aniž byste museli přepisovat veškerý throw() kód najednou.

Kompilátor nyní také diagnostikuje více neodpovídajících specifikací výjimek v deklarací v režimu C++17 nebo s /permissive- novým upozorněním C5043.

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

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 chyby odebrat, když stále používáte /std:c++17, přidejte /Zc:noexceptTypes- přepínač na příkazový řádek nebo aktualizujte kód tak, aby 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 teď implicitně inline, což znamená, že jejich deklarace v rámci třídy je nyní jejich definicí. Použití definice mimo řádek pro static constexpr datový člen je redundantní a nyní zastaralé. V sadě Visual Studio 2017 verze 15.5 se při /std:c++17 použití přepínače zobrazí následující kód 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 upozornění ve výchozím nastavení zapnuté. Další informace najdete v tématu Nové upozornění na __declspec atributy.

Výchozí funkce a __declspec(nothrow)

Kompilátor dříve povolil deklarování výchozích funkcí s __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. Pokud dojde k neshodě specifikací výjimek, standard vyžaduje, aby tyto funkce byly definovány jako odstraněné. V části /std:c++17vyvolá následující kód 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.
}

Pokud chcete tento kód opravit, odeberte __declspec(nothrow) z výchozí funkce, nebo odeberte = default a zadejte definici funkce spolu s případným 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ů se částečné specializace pro odpovídající konkrétní "volatelné" typy nemusí zkompilovat nebo selhat při výběru primární šablony, protože chybí částečná specializace pro ukazatele-to-noexcept-functions.

V takových případech může být nutné přidat více částečných specializace pro zpracování noexcept ukazatelů na funkce a noexcept ukazatelů na členské funkce. Tato přetížení jsou v režimu nebo novější pouze z právních předpisů /std:c++17 . Pokud musí být zachována zpětná kompatibilita s C++14 a píšete kód, který ostatní využívají, měli byste tato nová přetížení chránit uvnitř #ifdef direktiv. Pokud pracujete v samostatném modulu, můžete místo použití #ifdef strážek jednoduše zkompilovat pomocí /Zc:noexceptTypes- přepínače.

Následující kód se zkompiluje v rámci /std:c++14 , ale selže v rámci /std:c++17 s 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 úspěšný v rámci /std:c++17 , 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 15.6

Základy knihovny C++17 V1

P0220R1 do standardu začleňuje technické specifikace Library Fundamentals pro C++17. Řeší aktualizace na <experimental/tuple>, , <experimental/optional>, <experimental/functional>, <experimental/any>, <experimental/string_view>, <experimental/memory>, <experimental/memory_resource>, a <experimental/algorithm>.

C++17: Vylepšení dedukce argumentu šablony třídy pro standardní knihovnu

P0739R0 Přesunutím adopt_lock_t na začátek seznamu parametrů pro scoped_lock povolíte konzistentní použití parametru scoped_lock. Povolit std::variant konstruktoru, aby se podílel na řešení přetížení ve více případech, aby bylo možné přiřazovat kopírování.

Vylepšení shody ve 15.7

C++17: Přeformulování dědění konstruktorů

P0136R1 určuje, že using deklarace, která pojmenovává konstruktor, nyní zviditelňuje odpovídající konstruktory základní třídy pro inicializace odvozené třídy místo deklarování více konstruktorů odvozených tříd. Toto přeformulování je změna oproti C++14. V sadě Visual Studio 2017 verze 15.7 a novějších /std:c++17 může být kód, který je platný v jazyce C++14 a používá 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 najdete v tématu Konstruktory.

C++17: Inicializace rozšířené agregace

P0017R1

Pokud je konstruktor základní třídy neveřejný, ale přístupný pro odvozenou třídu, pak /std:c++17 v režimu a novějším v sadě Visual Studio 2017 verze 15.7 už nemůžete k inicializaci objektu odvozeného typu používat prázdné složené závorky. Následující příklad ukazuje chování vyhovují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 C++17 Derived se nyní považuje za agregační typ. To znamená, že inicializace prostřednictvím soukromého výchozího Base konstruktoru probíhá přímo jako součást rozšířeného pravidla inicializace agregace. Base Dříve byl privátní konstruktor volána prostřednictvím konstruktoru Derived a uspěla kvůli spřátelili deklaraci. 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: Deklarace netypových parametrů šablony s automatickým

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 efektů 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) metody je vázáno na example(void*);. V sadě Visual Studio 2017 verze 15.7 se v /std:c++17 režimu example nejlépe shoduje š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 nízké úrovně 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: Jak zabránit zbytečnému rozpadu (částečnému)

P0777R1 Přidává rozlišení mezi konceptem "rozpadu" a konceptem prostého odstranění konstantních nebo referenčních kvalifikátorů. Nová vlastnost remove_reference_tdecay_t typu nahrazuje v některých kontextech. Podpora je remove_cvref_t implementovaná 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 nová přetížení do std::hypot, pro typy float, doublea long double, z nichž každé má tři vstupní parametry.

C++17: <filesystem>

P0218R1 Přejímá systém souborů TS do standardu s několika úpravami formulace.

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

P0226R1 Do standardní <cmath> hlavičky přijímá předchozí technické specifikace pro matematické speciální funkce.

C++17: Příručky pro dedukci pro standardní knihovnu

P0433R2 Aktualizace na STL, aby bylo možné využít C++17 přijetí P0091R3, které přidává podporu pro dedukci argumentů šablony třídy.

C++17: Oprava převodů elementární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 používala std::errcstd::error_code.

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

P0426R1std::traits_type Změny členských funkcí length, compare, a find tak, aby byly std::string_view použitelné v konstantních výrazech. (V sadě Visual Studio 2017 verze 15.6 se podporuje pouze 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 se výchozí argument neignoruje:

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

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

Překlad závislých ip adres

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ších přeloží D::type na B<T>::type v šabloně primární třídy na .

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 typenamevyvolá kompilátor upozornění C4346: 'B<T*>::type': dependent name is not a type a chybu 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 voidnebo předáním /wd:4834 do 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 variadické šablony

V předchozích edicích sady Visual Studio byl chybně povolen seznam inicializace základní třídy konstruktoru variadické šablony, ve které chyběly argumenty šablony. V sadě Visual Studio 2017 verze 15.7 je vyvolána 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;

Pokud chcete chybu opravit, změňte výraz na B()B<T>().

constexpr inicializace agregace

Předchozí verze kompilátoru C++ nesprávně zpracovávaly constexpr agregační inicializaci. Kompilátor přijal neplatný kód, ve kterém agregace-init-list měla 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 objektu std::array s vnořenou složenou závorkou init-lists dejte vnitřní matici vlastní složenou závorku:

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

typename u nekvalifikovaných identifikátorů

V /permissive- režimu už kompilátor nepřijímá nechybná typename klíčová 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 aliasů už není povolený. Dříve kompilátor tento kód akceptoval, ale ignoroval. Při použití aliasu by se upozornění na vyřazení nikdy nezpozorovalo.

Místo toho se může použít standardní atribut [[deprecated]] C++, který se v sadě Visual Studio 2017 verze 15.6 respektuje. 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 znakem =v definici aliasu):

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

Diagnostika dvoufázového vyhledávání názvů

Dvoufázové vyhledávání názvů vyžaduje, aby nezávisející názvy používané v těle šablony byly v době definice šablony viditelné. Dříve by kompilátor Microsoft C++ ponechal nenalezený název, který nebyl vyhledán až do doby vytvoření instance. Teď vyžaduje, aby v těle šablony byly vázané názvy, které nejsou závislé.

Jedním ze způsobů, jak se to může manifestovat, je vyhledávání do závislých základních tříd. Dříve kompilátor povolil použití názvů, které jsou definovány v závislých základních třídách. Je to proto, že se vyhledávají během doby vytváření instancí, když se vyřeší všechny typy. Tento kód se teď považuje za chybu. V těchto případech můžete vynutit vyhledání proměnné v době vytvoření instance tak, že ji kvalifikujete pomocí typu základní třídy nebo ji jinak nastavíte jako závislou this-> , například přidáním 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
    }
};

Pokud chcete chybu opravit, změňte příkaz na returnreturn this->base_value;.

Poznámka

Ve verzích knihovny Boost.Python starších než 1.70 existuje alternativní řešení specifické pro MSVC pro dopřednou deklaraci šablony v unwind_type.hppnástroji . V /permissive- režimu začínajícím v sadě Visual Studio 2017 verze 15.8 (_MSC_VER==1915) kompilátor MSVC správně provede vyhledávání názvů závislých na argumentech (ADL). Teď je konzistentní s ostatními kompilátory, takže tato ochrana alternativního řešení je zbytečná. Pokud se chcete vyhnout chybě C3861: 'unwind_type': identifier not found, aktualizujte knihovnu Boost.Python.

forward – deklarace a definice v oboru názvů std

Standard C++ neumožňuje uživateli přidat do oboru názvů stddopředné 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ď má za následek nedefinované chování.

Někdy v budoucnu microsoft přesune umístění, kde jsou definovány některé standardní typy knihoven. Tato změna způsobí narušení existujícího kódu, který dopředné deklarace přidá 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 ve výchozím nastavení je vypnuté. Bude mít vliv na programy kompilované pomocí /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`
}

Chybu opravíte tak, že místo dopředné #include deklarace použijete direktivu:

#include <vector>

Konstruktory, které delegují na sebe

Standard jazyka C++ navrhuje, aby kompilátor vygeneroval diagnostiku, když delegující konstruktor deleguje sám na sebe. Kompilátor Microsoft C++ v /std:c++17 režimech a /std:c++latest teď 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
};

Pokud se chcete vyhnout nekonečné smyčce, delegujte ho do jiného konstruktoru:

class X {
public:

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

offsetof s konstantními výrazy

offsetof se tradičně implementuje pomocí makra, které vyžaduje reinterpret_cast. Toto použití je neplatné v kontextech, které vyžadují konstantní výraz, ale kompilátor jazyka Microsoft C++ to tradičně povoluje. Makro offsetof , které je dodává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 zobrazit ve výchozím režimu, a pomáhá tak kódu odpovídat standardnímu chování jazyka C++. V rámci /permissive-jsou omezení ještě přísnější. Použití výsledku na offsetof místech, která vyžadují konstantní výrazy, může mít za následek kód, který vydá upozornění C4644 nebo C2975.

Následující kód vyvolá C4644 ve výchozích režimech 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;
    }
}

Chybu opravíte tak offsetof , jak je definováno prostřednictvím <cstddef>:

#include <cstddef>

struct Data {
    int x;
};

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

kvalifikátory cv u základních tříd, které podléhají rozšíření balíčku

Předchozí verze kompilátoru 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 specifikátory vnořeného názvu

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

Následující kód v /permissive- režimu teď vyvolá 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 15.9

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

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

  • pokud je jedním z výrazů operandu 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 , když nemůže zaručit vyhodnocení zleva doprava. Kompilátor vygeneruje toto upozornění pouze v případě, že /std:c++17 je zadáno nebo později, protože požadavek na pořadí zleva doprava těchto operátorů byl zaveden v C++17.

Pokud chcete toto upozornění vyřešit, nejprve zvažte, jestli je nutné vyhodnotit operandy zleva doprava. Může to být například nezbytné, když vyhodnocení operandů může vést k vedlejším účinkům závislým na pořadí. Pořadí, ve kterém se operandy vyhodnocují, 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 odkazem 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ů

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

V předchozích verzích kompilátoru byl povolen následující kód. V sadě Visual Studio 2017 verze 15.9 kompilátor v /permissive- režimu 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;

Pokud chcete chybu opravit, deklarujte from_template před from_template_t.

Změny modulů

V sadě Visual Studio 2017 verze 15.9 kompilátor vyvolá C5050 pokaždé, když možnosti příkazového řádku pro moduly nejsou konzistentní mezi vytvořením modulu a stranami využití modulu. 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 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 každé .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 za hash a pak se zkontroluje podle hodnoty hash zadané 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 nepřevodené kontexty

Implementace se liší v pravidlech částečného řazení zahrnujících aliasy v nededukovaných kontextech. V následujícím příkladu GCC a kompilátor Microsoft C++ (v /permissive- režimu) vyvolá chybu, zatímco Clang přijme 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ává 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 v implementaci jsou způsobené regresí ve standardním znění jazyka C++. Řešení základního problému 2235 odebralo text, který by umožnil řazení těchto přetížení. Aktuální standard jazyka C++ neposkytuje mechanismus pro částečné řazení těchto funkcí, takže se považují za nejednoznačné.

Jako alternativní řešení doporučujeme, abyste při řešení tohoto problému nespoléhali na částečné řazení. Místo toho pomocí SFINAE odstraňte konkrétní 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 typy, které nejsou literály v definicích funkcí s šablonou

Neplatné výrazy a typy, které nejsou literály, jsou nyní správně diagnostikovány v definicích funkcí šablon, které jsou explicitně specializované. Dříve se tyto chyby pro definici funkce nevygenerovaly. Pokud by se však vyhodnotil jako součást konstantního výrazu, byl by stále diagnostikován neplatný výraz nebo typ, který není literálový.

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

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 kód vyvolá 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'.

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

Viz také

Shoda jazyka Microsoft C/C++