Miglioramenti della conformità C++, modifiche al comportamento e correzioni di bug in Visual Studio 2017

Microsoft C/C++ in Visual Studio (MSVC) apporta miglioramenti alla conformità e correzioni di bug in ogni versione. Questo articolo elenca i miglioramenti introdotti a livello di versione principale e secondaria. Per passare direttamente alle modifiche per una versione specifica, usare l'elenco seguente in questo articolo.

Questo documento elenca le modifiche in Visual Studio 2017. Per una guida alle modifiche in Visual Studio 2022, vedere Miglioramenti della conformità C++ in Visual Studio 2022. Per una guida alle modifiche in Visual Studio 2019, vedere Miglioramenti della conformità C++ in Visual Studio 2019. Per un elenco completo dei miglioramenti della conformità precedenti, vedere Visual C++ What's New 2003 -2015.

Miglioramenti della conformità in Visual Studio 2017 RTW (versione 15.0)

Con il supporto per l'inizializzazione dei membri dati generalizzati constexpr e non statici (NSDMI) per le aggregazioni, il compilatore MSVC in Visual Studio 2017 è ora completo per le funzionalità aggiunte nello standard C++14. Nel compilatore, tuttavia, mancano ancora alcune funzionalità relative agli standard C++11 e C++98. Vedere Conformità del linguaggio Microsoft C/C++ per lo stato corrente del compilatore.

C++11: Supporto di Expression SFINAE in più librerie

Il compilatore continua a migliorare il supporto per l'espressione SFINAE. È necessario per la deduzione e la sostituzione degli argomenti modello in cui decltype e constexpr le espressioni possono essere visualizzate come parametri di modello. Per altre informazioni, vedere il blog relativo ai miglioramenti dell'espressione SFINAE in Visual Studio 2017 RC.

C++14: NSDMI per le aggregazioni

Un'aggregazione è una matrice o una classe che include: nessun costruttore fornito dall'utente, nessun membro dati non statico che sono privati o protetti, nessuna classe di base e nessuna funzione virtuale. A partire da C++14, le aggregazioni possono contenere inizializzatori membri. Per altre informazioni, vedere l'argomento relativo a inizializzatori di membro e aggregati.

C++14: Esteso constexpr

Le espressioni dichiarate come constexpr sono ora consentite per contenere determinati tipi di dichiarazioni, se e istruzioni switch, istruzioni ciclo e mutazione di oggetti la cui durata è iniziata all'interno della valutazione dell'espressione constexpr . Non è più necessario che una constexpr funzione membro non statica sia implicitamente const. Per altre informazioni, vedere Riduzione dei vincoli sulle constexpr funzioni.

C++17: Terse static_assert

Il parametro di messaggio per static_assert è facoltativo. Per altre informazioni, vedere N3928: Estensione di static_assert, v2.

C++17: Attributo [[fallthrough]]

In /std:c++17 modalità e versioni successive, l'attributo [[fallthrough]] può essere usato nel contesto delle istruzioni switch come hint al compilatore che il comportamento fall-through è previsto. L'attributo impedisce al compilatore di emettere avvisi in tali casi. Per altre informazioni, vedere P0188R0 - Wording for [[fallthrough]] attribute.

Cicli basati su for intervalli generalizzati

I cicli basati sull'intervallo for non richiedono più che begin() e end() restituiscono oggetti dello stesso tipo. Questa modifica consente di end() restituire un sentinel come usato dagli intervalli in range-v3 e la specifica tecnica degli intervalli completata, ma non abbastanza pubblicata. Per altre informazioni, vedere P0184R0 - Generalizing the Range-Based for Loop.

Copy-list-initialization

Visual Studio 2017 genera correttamente errori del compilatore correlati alla creazione di oggetti usando elenchi di inizializzatori. Questi errori non sono stati rilevati in Visual Studio 2015 e potrebbero causare arresti anomali o comportamenti di runtime non definiti. N4594 13.3.1.7p1In , copy-list-initializationil compilatore deve considerare un costruttore esplicito per la risoluzione dell'overload. Tuttavia, deve generare un errore se tale overload specifico viene scelto.

I seguenti due esempi vengono compilati in Visual Studio 2015 ma non in Visual Studio 2017.

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

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

}

Per correggere l'errore, usare l'inizializzazione diretta:

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

In Visual Studio 2015 il compilatore ha gestito erroneamente l'inizializzazione copy-list nello stesso modo dell'inizializzazione regolare della copia: ha considerato solo la conversione dei costruttori per la risoluzione dell'overload. Nell'esempio seguente Visual Studio 2015 sceglie MyInt(23). Visual Studio 2017 genera correttamente l'errore.

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

Questo esempio è simile a quello precedente, ma viene generato un errore diverso. Ha esito positivo in Visual Studio 2015 e ha esito negativo in Visual Studio 2017 con C2668.

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

struct B {
    B(int) {}
};

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

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

Typedef deprecati

Visual Studio 2017 genera ora l'avviso corretto per i typedef deprecati dichiarati in una classe o in uno struct. L'esempio seguente viene compilato senza avvisi in Visual Studio 2015. Produce C4996 in Visual Studio 2017.

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

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

constexpr

Visual Studio 2017 genera correttamente un errore quando l'operando a sinistra di un'operazione di valutazione condizionale non è valida in un constexpr contesto. Il codice seguente viene compilato in Visual Studio 2015, ma non in Visual Studio 2017, in cui genera 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
}

Per correggere l'errore, dichiarare la funzione array::size() come constexpr o rimuovere il qualificatore constexpr da f.

Tipi di classe passati alle funzioni variadic

In Visual Studio 2017, le classi o gli struct passati a una funzione variadic, ad esempio printf , devono essere facilmente copiabili. Quando tali oggetti vengono passati, il compilatore esegue semplicemente una copia bit per bit e non chiama il costruttore o il distruttore.

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

Per correggere l'errore, è possibile chiamare una funzione membro che restituisca un tipo facilmente copiabile

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

o esegua un cast statico per convertire l'oggetto prima di passarlo:

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

Per le stringhe compilate e gestite usando CString, è necessario usare l'oggetto fornito operator LPCTSTR() per eseguire il cast di un CString oggetto al puntatore C previsto dalla stringa di formato.

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

Qualificatori CV nella costruzione di classi

In Visual Studio 2015 il compilatore a volte ignora erroneamente il qualificatore CV quando genera un oggetto classe usando una chiamata al costruttore. Questo problema può causare un arresto anomalo o un comportamento imprevisto in fase di esecuzione. L'esempio seguente viene compilato in Visual Studio 2015, ma genera un errore del compilatore in Visual Studio 2017:

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

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

Per correggere l'errore, dichiarare operator int() come const.

Controllo dell'accesso per i nomi completi nei modelli

Le versioni precedenti del compilatore non controllano l'accesso ai nomi qualificati in alcuni contesti di modello. Questo problema può interferire con il comportamento SFINAE previsto, in cui la sostituzione dovrebbe non riuscire a causa dell'inaccessibilità di un nome. Potrebbe avere potenzialmente causato un arresto anomalo o un comportamento imprevisto in fase di esecuzione, perché il compilatore ha chiamato erroneamente l'overload errato dell'operatore. In Visual Studio 2017 viene generato un errore del compilatore. L'errore specifico può variare, ma un errore tipico è C2672, "nessuna funzione di overload corrispondente trovata". Il codice seguente viene compilato in Visual Studio 2015 ma genera un errore in Visual Studio 2017:

#include <type_traits>

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

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

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

Elenchi di argomenti di modello mancanti

In Visual Studio 2015 e versioni precedenti il compilatore non ha diagnosticato tutti gli elenchi di argomenti modello mancanti. Non si noti quando il modello mancante è apparso in un elenco di parametri di modello: ad esempio, quando parte di un argomento modello predefinito o un parametro modello non di tipo mancava. Questo problema può causare un comportamento imprevedibile, compresi arresti anomali del compilatore o il comportamento imprevisto in fase di esecuzione. Il codice seguente viene compilato in Visual Studio 2015, ma genera un errore in Visual Studio 2017.

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

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

Espressione SFINAE

Per supportare l'espressione SFINAE, il compilatore ora analizza argomenti decltype quando i modelli vengono dichiarati senza creare un'istanza. Quindi, se nell'argomento viene trovata una specializzazione non dipendente, non viene posticipata fino a quando non viene eseguita un'istanza decltype . Viene elaborata immediatamente ed è possibile diagnosticare eventuali errori risultanti in quel momento.

Nell'esempio seguente viene illustrato questo tipo di errore del compilatore che viene generato al momento della dichiarazione:

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

Classi dichiarate in spazi dei nomi anonimi

In base allo standard C++, una classe dichiarata all'interno di uno spazio dei nomi anonimo include un collegamento interno e pertanto non può essere esportata. In Visual Studio 2015 e versioni precedenti questa regola non viene applicata. In Visual Studio 2017 la regola viene applicata parzialmente. In Visual Studio 2017 l'esempio seguente genera l'errore C2201:

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

Inizializzatori predefiniti per i membri della classe di valori (C++/CLI)

In Visual Studio 2015 e versioni precedenti il compilatore consentiva, ma ignorava, un inizializzatore di membro predefinito per un membro di una classe di valori. L'inizializzazione predefinita di una classe di valori inizializza sempre a zero i membri. Non è consentito un costruttore predefinito. In Visual Studio 2017 gli inizializzatori di membri predefiniti generano un errore del compilatore, come illustra l'esempio seguente:

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

Indicizzatori predefiniti (C++/CLI)

In Visual Studio 2015 e versioni precedenti il compilatore in alcuni casi identificava erroneamente una proprietà predefinita come indicizzatore predefinito. È stato possibile risolvere il problema usando l'identificatore default per accedere alla proprietà. Questa soluzione è diventata problematica dopo che default è stato introdotto come parola chiave in C++11. In Visual Studio 2017 i bug che hanno richiesto la soluzione alternativa sono stati corretti. Il compilatore genera ora un errore quando default viene usato per accedere alla proprietà predefinita per una classe.

//class1.cs

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

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

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

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

In Visual Studio 2017 è possibile accedere a entrambe le proprietà Value in base al nome:

#using "class1.dll"

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

Miglioramenti della conformità nella versione 15.3

constexpr lambda

È ora possibile usare espressioni lambda nelle espressioni costanti. Per altre informazioni, vedere constexpr espressioni lambda in C++.

if constexpr nei modelli di funzioni

Un modello di funzione può contenere istruzioni if constexpr per abilitare il branching in fase di compilazione. Per altre informazioni, vedere if constexpr istruzioni.

Istruzioni di selezione con inizializzatori

Un'istruzione if può includere un inizializzatore che introduce una variabile nell'ambito del blocco all'interno dell'istruzione stessa. Per altre informazioni, vedere if istruzioni con inizializzatore.

Attributi [[maybe_unused]] e [[nodiscard]]

Il nuovo attributo [[maybe_unused]] disattiva gli avvisi quando non viene usata un'entità. L'attributo [[nodiscard]] crea un avviso se il valore restituito di una chiamata a una funzione viene rimosso. Per altre informazioni, vedere Attributes in C++ (Attributi in C++).

Uso degli spazi dei nomi degli attributi senza ripetizione

Nuova sintassi per consentire un solo identificatore dello spazio dei nomi in un elenco di attributi. Per altre informazioni, vedere Attributes in C++ (Attributi in C++).

Binding strutturati

In una singola dichiarazione è ora possibile archiviare un valore con i nomi singoli dei relativi componenti, quando il valore è una matrice, std::tuple o std::pair, oppure tutti i membri dati sono non statici e pubblici. Per altre informazioni, vedere P0144R0 - Structured Bindings e Restituire più valori da una funzione.

Regole di costruzione per i valori di enum class

È ora disponibile una conversione implicita per le enumerazioni con ambito che non si restringe. Converte da un tipo sottostante di enumerazione con ambito all'enumerazione stessa. La conversione è disponibile quando la definizione non introduce un enumeratore e quando l'origine usa una sintassi di inizializzazione elenco. Per altre informazioni, vedere P0138R2 - Construction Rules for enum class Values e enumerazioni.

Acquisizione di *this per valore

L'oggetto *this in un'espressione lambda può ora essere acquisito per valore. Questa modifica rende possibili scenari in cui l'espressione lambda viene richiamata in operazioni parallele e asincrone, in particolare nelle architetture di computer più recenti. Per altre informazioni, vedere P0018R3 - Lambda Capture of *this by Value as [=,*this].

Rimozione di operator++ per bool

operator++ non è più supportato nei tipi bool. Per altre informazioni, vedere P0002R1 - Remove Deprecated operator++(bool).

Rimozione della parola chiave register deprecata

La parola chiave register, precedentemente deprecata e ignorata dal compilatore, è stata ora rimossa dal linguaggio. Per altre informazioni, vedere P0001R1 - Remove Deprecated Use of the register Keyword.

Chiamate a modelli di membro eliminati

Nelle versioni precedenti di Visual Studio, il compilatore in alcuni casi non riesce a generare un errore per le chiamate non formatte a un modello membro eliminato. Queste chiamate potrebbero causare arresti anomali in fase di esecuzione. Il codice seguente produce ora 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
}

Per correggere l'errore, dichiarare i come int.

Controlli sulle precondizioni per tratti di tipo

Visual Studio 2017 versione 15.3 migliora i controlli sulle precondizioni per i tratti di tipo in modo da attenersi più rigorosamente agli standard. Un controllo di questo tipo verifica ad esempio che il tratto di tipo sia assegnabile. Il codice seguente produce C2139 in Visual Studio 2017 versione 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

Nuovi controlli di avviso e di runtime del compilatore sul marshalling da nativo a gestito

Le chiamate da funzioni gestite a funzioni native richiedono il marshalling. CLR esegue il marshalling ma non comprende la semantica di C++. Se si passa un oggetto nativo per valore, CLR chiama il costruttore di copia dell'oggetto o usa BitBlt, da cui può derivare un comportamento non definito in fase di runtime.

Ora il compilatore genera un avviso se trova questo errore in fase di compilazione: un oggetto nativo con ctor di copia eliminato viene passato tra un limite nativo e gestito in base al valore. Se il compilatore non viene a conoscenza di questo evento in fase di runtime, inserisce un controllo di runtime in modo che il programma chiami immediatamente std::terminate nel momento in cui si verifica un marshalling non valido. In Visual Studio 2017 versione 15.3 il codice seguente genera l'avviso 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.`
}

Per correggere l'errore, rimuovere la direttiva #pragma managed per contrassegnare il chiamante come nativo ed evitare il marshalling.

Avviso di API sperimentale per WinRT

Le API WinRT rilasciate a scopo di sperimentazione e feedback vengono contrassegnate con Windows.Foundation.Metadata.ExperimentalAttribute. In Visual Studio 2017 versione 15.3 il compilatore genera un avviso C4698 per questo attributo. Alcune API presenti in versioni precedenti di Windows SDK sono già state contrassegnate con questo attributo e le chiamate a queste API attivano ora l'avviso del compilatore. Gli SDK Windows più recenti hanno l'attributo rimosso da tutti i tipi forniti. Se si usa un SDK precedente, è necessario eliminare questi avvisi per tutte le chiamate ai tipi forniti.

Il codice seguente genera l'avviso 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

Per disabilitare l'avviso, aggiungere #pragma:

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

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

#pragma warning(pop)

Definizione out-of-line di una funzione membro di modello

Visual Studio 2017 versione 15.3 genera un errore per una definizione fuori riga di una funzione membro modello che non è stata dichiarata nella classe. Il codice seguente genera ora l'errore C2039:

struct S {};

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

Per correggere l'errore, aggiungere una dichiarazione alla classe:

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

Tentativo di accettare l'indirizzo del puntatore this

In C++, this è un puntatore di tipo a X. Non è possibile accettare l'indirizzo di this o associarlo a un riferimento lvalue. Nelle versioni precedenti di Visual Studio, il compilatore consentiva di ignorare questa restrizione usando un cast. In Visual Studio 2017 versione 15.3 il compilatore genera l'errore C2664.

Conversione a una classe di base inaccessibile

Visual Studio 2017 versione 15.3 genera un errore quando si prova a convertire un tipo in una classe di base inaccessibile. Il codice seguente non è valido e può causare un arresto anomalo in fase di runtime. Il compilatore produce ora C2243 quando visualizza il codice simile al seguente:

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

Gli argomenti predefiniti non sono consentiti nelle definizioni out-of-line di funzioni membro

Gli argomenti predefiniti non sono consentiti nelle definizioni out-of-line di funzioni membro presenti in classi modello. Il compilatore emetterà un avviso in /permissivee un errore rigido in /permissive-.

Nelle versioni precedenti di Visual Studio, il codice non valido seguente poteva causare potenzialmente un arresto anomalo in fase di runtime. Visual Studio 2017 versione 15.3 genera avviso 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
{
    // ...
}

Per correggere l'errore, rimuovere l'argomento predefinito = false.

Uso di offsetof con indicatore di membro composto

In Visual Studio 2017 versione 15.3, usando offsetof(T, m) dove m è un "designatore membro composto" genera un avviso quando si compila con l'opzione /Wall . Il codice seguente non è valido e può causare un arresto anomalo in fase di runtime. Visual Studio 2017 versione 15.3 genera avviso 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]);

Per correggere il codice, disabilitare l'avviso con un pragma o modificare il codice in modo da non usare offsetof:

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

Uso di offsetof con una funzione membro o un membro dati statici

In Visual Studio 2017 versione 15.3 l'uso di offsetof(T, m) dove m fa riferimento a un membro dati statici o a una funzione membro determina un errore. Il codice seguente genera l'errore 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'

Questo codice non è valido e può causare un arresto anomalo in fase di runtime. Per correggere l'errore, modificare il codice in modo da non richiamare più un comportamento indefinito. Si tratta di codice non portabile non consentito dallo standard C++.

Nuovo avviso per attributi __declspec

In Visual Studio 2017 versione 15.3 il compilatore non ignora più gli attributi se prima della specifica di collegamento extern "C" viene applicato __declspec(...). In precedenza, il compilatore avrebbe ignorato l'attributo, con possibili implicazioni in fase di runtime. Quando vengono impostate le opzioni e/WX, il codice seguente genera l'avviso /WallC4768:

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

Per risolvere il problema, inserire prima extern "C":

extern "C" __declspec(noinline) HRESULT __stdcall

Questo avviso è disattivato per impostazione predefinita in Visual Studio 2017 versione 15.3 e influisce solo sul codice compilato con /Wall/WX. A partire da Visual Studio 2017 versione 15.5, è abilitato per impostazione predefinita come avviso di livello 3.

decltype e chiamate a distruttori eliminati

Nelle versioni precedenti di Visual Studio, il compilatore non era in grado di rilevare quando si verificava una chiamata a un distruttore eliminato nel contesto dell'espressione associata a decltype. In Visual Studio 2017 versione 15.3 il codice seguente genera l'errore 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
}

Variabili const non inizializzate

Visual Studio 2017 RTW ha avuto una regressione: il compilatore C++ non emetteva una diagnostica per una variabile non inizializzata const . Questa regressione è stata risolta in Visual Studio 2017 versione 15.3. Il codice seguente genera ora l'avviso C4132:

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

Per correggere l'errore, assegnare un valore a Value.

Dichiarazioni vuote

Visual Studio 2017 versione 15.3 genera ora un avviso in presenza di dichiarazioni vuote per tutti i tipi, non solo per i tipi predefiniti. Il codice seguente genera ora un avviso di livello 2 C4091 per tutte e quattro le dichiarazioni:

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

Per rimuovere gli avvisi, impostare le dichiarazioni vuote come commento o rimuoverle. Se l'oggetto senza nome deve avere un effetto collaterale (ad esempio, RAII), è opportuno assegnare un nome all'oggetto.

L'avviso viene escluso /Wv:18 in e viene attivato per impostazione predefinita a livello di avviso W2.

std::is_convertible per i tipi matrice

Le versioni precedenti del compilatore hanno dato risultati non corretti per std::is_convertible i tipi di matrice. Per questo motivo, gli autori delle librerie dovevano gestire come caso speciale il compilatore Microsoft C++ quando veniva usato il tratto di tipo std::is_convertible<...>. Nell'esempio seguente le asserzioni statiche hanno esito positivo nelle versioni precedenti di Visual Studio, ma negativo in Visual Studio 2017 versione 15.3:

#include <type_traits>

using Array = char[1];

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

std::is_convertible<From, To> viene calcolato controllando se una definizione di funzione immaginaria è ben formata:

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

Distruttori privati e std::is_constructible

Le versioni precedenti del compilatore ignorano se un distruttore è privato quando decide il risultato di std::is_constructible. Questa condizione viene ora presa in considerazione. Nell'esempio seguente le asserzioni statiche hanno esito positivo nelle versioni precedenti di Visual Studio, ma negativo in Visual Studio 2017 versione 15.3:

#include <type_traits>

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

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

A causa dei distruttori privati un tipo può essere non costruibile. std::is_constructible<T, Args...> viene calcolato come se fosse scritta la dichiarazione seguente:

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

Questa chiamata implica una chiamata al distruttore.

C2668: risoluzione ambigua dell'overload

Le versioni precedenti del compilatore talvolta non riescono a rilevare ambiguità quando vengono trovati più candidati sia tramite dichiarazioni sia con ricerche dipendenti dall'argomento. Ciò può portare alla scelta di un overload non corretto e a un comportamento di runtime imprevisto. Nell'esempio seguente Visual Studio 2017 versione 15.3 genera correttamente 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
}

Per correggere il codice, rimuovere l'istruzione using N::f se l'intenzione era di chiamare ::f().

C2660: dichiarazioni di funzione locali e ricerca dipendente dall'argomento

Le dichiarazioni di funzione locali nascondono la dichiarazione di funzione nell'ambito che la contiene e disabilitano la ricerca dipendente dall'argomento. Le versioni precedenti del compilatore hanno sempre eseguito la ricerca dipendente dall'argomento in questo caso. Potrebbe causare un comportamento di runtime imprevisto, se il compilatore ha scelto l'overload errato. In genere, l'errore è dovuto a una firma non corretta della dichiarazione di funzione locale. Nell'esempio seguente Visual Studio 2017 versione 15.3 genera correttamente 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);
}

Per risolvere il problema, modificare la firma f(S) o rimuoverla.

C5038: ordine di inizializzazione negli elenchi di inizializzatori

I membri della classe vengono inizializzati nell'ordine dichiarato, non l'ordine visualizzato negli elenchi di inizializzatori. Le versioni precedenti del compilatore non avvisano quando l'ordine dell'elenco di inizializzatori è diverso dall'ordine di dichiarazione. Questo problema potrebbe causare un comportamento di runtime non definito se l'inizializzazione di un membro dipende da un altro membro nell'elenco già inizializzato. Nell'esempio seguente Visual Studio 2017 versione 15.3 (con /Wall) genera l'avviso 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;
};

Per risolvere il problema, disporre l'elenco di inizializzatori nello stesso ordine delle dichiarazioni. Viene generato un avviso analogo quando uno o entrambi gli inizializzatori fanno riferimento a membri della classe di base.

Questo avviso è disattivato per impostazione predefinita e influisce solo sul codice compilato con /Wall.

Miglioramenti della conformità nella versione 15.5

Le funzionalità contrassegnate con [14] sono disponibili in modo incondizionato anche in /std:c++14 modalità.

Nuova opzione del compilatore per extern constexpr

Nelle versioni precedenti di Visual Studio, il compilatore ha sempre dato un constexpr collegamento interno variabile, anche quando la variabile è stata contrassegnata extern. In Visual Studio 2017 versione 15.5, un nuovo commutatore del compilatore, /Zc:externConstexprconsente un comportamento corretto e conforme agli standard. Per altre informazioni, vedere extern constexpr collegamento.

Rimozione delle specifiche di eccezione dinamiche

P0003R5 Le specifiche di eccezione dinamiche sono deprecate in C++11. La funzionalità viene rimossa da C++17, ma la specifica deprecata throw() (ancora) viene mantenuta rigorosamente come alias per noexcept(true). Per altre informazioni, vedere Rimozione e rimozione dinamica delle specifiche delle eccezioni.noexcept

not_fn()

P0005R4not_fn è una sostituzione di not1 e not2.

Riformulazione di enable_shared_from_this

P0033R1enable_shared_from_this è stato aggiunto in C++11. Lo standard C++17 aggiorna la specifica per gestire meglio determinati casi limite. [14]

Splicing di mappe e set

P0083R3 Questa funzionalità consente l'estrazione di nodi da contenitori associativi, ovvero , map, setunordered_map, unordered_setche possono essere modificati e inseriti nuovamente nello stesso contenitore o in un contenitore diverso che usa lo stesso tipo di nodo. Un caso d'uso comune è l'estrazione di un nodo da std::map, la modifica della chiave e il reinserimento.

Deprecazione di parti superflue della libreria

P0174R2 Diverse funzionalità della libreria standard C++ sono state sostituite da funzionalità più recenti nel corso degli anni oppure si sono rivelate poco utili o problematiche. Queste funzionalità sono ufficialmente deprecate in C++ 17.

Rimozione del supporto dell'allocatore in std::function

P0302R1 Prima di C++17, il modello std::function di classe aveva diversi costruttori che hanno preso un argomento allocatore. L'uso di allocatori in questo contesto era tuttavia problematico e la semantica non era chiara. I costruttori problematici sono stati rimossi.

Correzioni per not_fn()

P0358R1 La nuova formulazione per std::not_fn offre il supporto della propagazione della categoria di valori quando è usata nella chiamata al wrapper.

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

P0414R2 Unione delle modifiche di shared_ptr da Library Fundamentals a C++17. [14]

Correzione di shared_ptr per le matrici

P0497R0 Correzioni al supporto di shared_ptr per le matrici. [14]

Chiarimento per insert_return_type

P0508R0 I contenitori associativi con chiavi univoche e i contenitori non ordinati con chiavi univoche hanno una funzione membro insert che restituisce un tipo annidato insert_return_type. Tale tipo restituito è ora definito come specializzazione di un tipo con parametri negli elementi Iterator e NodeType del contenitore.

Variabili inline per la libreria standard

Per P0607R0, diverse variabili comuni dichiarate nella libreria standard sono ora dichiarate inline.

Funzionalità Annex D deprecate

Annex D dello standard C++ contiene tutte le funzionalità deprecate, incluse shared_ptr::unique(), <codecvt> e namespace std::tr1. Quando l'opzione del /std:c++17 compilatore o successiva è impostata, quasi tutte le funzionalità della libreria standard in Allegato D sono contrassegnate come deprecate. Per altre informazioni, vedere Funzionalità della libreria Standard nell'allegato D contrassegnate come deprecate.

Lo std::tr2::sys spazio dei nomi in <experimental/filesystem> ora genera un avviso /std:c++14 di deprecazione per impostazione predefinita ed è ora rimosso /std:c++17 in e versioni successive per impostazione predefinita.

La conformità negli elementi <iostream> è stata migliorata evitando un'estensione non standard (specializzazioni esplicite in-class).

La libreria standard ora usa modelli di variabile internamente.

La libreria standard è stata aggiornata in risposta alle modifiche del compilatore C++17. Aggiornamenti includere l'aggiunta di noexcept nel sistema di tipi e la rimozione di specifiche di eccezione dinamica.

Modifica di ordinamento parziale

Il compilatore ora rifiuta correttamente il codice seguente e visualizza il messaggio di errore corretto:

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

Il problema nell'esempio precedente è la presenza di due differenze nei tipi (const rispetto a non-const e pack rispetto a non-pack). Per eliminare l'errore del compilatore, rimuovere una delle differenze. Il compilatore può quindi ordinare senza ambiguità le funzioni.

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

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

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

Gestori di eccezioni

I gestori del riferimento al tipo di matrice o di funzione non sono mai una corrispondenza per gli oggetti eccezione. Il compilatore ora rispetta correttamente questa regola e genera un avviso di livello 4, C4843. Non corrisponde più a un gestore di char* o wchar_t* a un valore letterale stringa quando /Zc:strictStrings viene usato.

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

Il codice seguente consente di evitare l'errore:

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

std::tr1 Lo spazio dei nomi è deprecato

Lo spazio dei nomi std::tr1 non standard è ora contrassegnato come deprecato in entrambe le modalità C++14 e C++17. In Visual Studio 2017 versione 15.5 il codice seguente genera l'errore 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.

Per correggere l'errore, rimuovere il riferimento allo spazio dei nomi tr1:

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

Le funzionalità della libreria standard nell'allegato D sono contrassegnate come deprecate

Quando viene impostata la modalità o l'opzione del compilatore successiva, quasi tutte le funzionalità della libreria standard nell'Allegato /std:c++17 D vengono contrassegnate come deprecate.

In Visual Studio 2017 versione 15.5 il codice seguente genera l'errore 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.

Per correggere l'errore, seguire le istruzioni nel testo dell'avviso, come illustrato nel codice seguente:

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

Variabili locali senza riferimenti

In Visual Studio 15.5 l'avviso C4189 viene generato in altri casi, come illustrato nel codice seguente:

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

Per correggere l'errore, rimuovere la variabile non usata.

Commenti a riga singola

In Visual Studio 2017 versione 15.5 gli avvisi C4001 e C4179 non vengono più generati dal compilatore C. In precedenza, sono stati generati solo sotto il commutatore del /Za compilatore. Gli avvisi non sono più necessari perché i commenti a riga singola fanno parte dello standard C da 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'

Quando il codice non deve essere compatibile con le versioni precedenti, evitare l'avviso rimuovendo l'eliminazione di C4001 e C4179. Se è necessario che il codice sia compatibile con le versioni precedenti, eliminare solo C4619.

/* C only */

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

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

Attributi __declspec con collegamento extern "C"

Nelle versioni precedenti di Visual Studio il compilatore ignora gli attributi __declspec(...) quando __declspec(...) viene applicato prima della specifica del collegamento extern "C". Questo comportamento causa la generazione di codice non desiderato dall'utente, con possibili implicazioni per il runtime. L'avviso C4768 è stato aggiunto in Visual Studio versione 15.3, ma è stato disattivato per impostazione predefinita. In Visual Studio 2017 versione 15.5 l'avviso è abilitato per impostazione predefinita.

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

Per correggere l'errore, inserire la specifica del collegamento prima dell'attributo __declspec:

extern "C" __declspec(noinline) HRESULT __stdcall

Questo nuovo avviso C4768 viene visualizzato in alcune intestazioni di Windows SDK fornite con Visual Studio 2017 15.3 o versione precedente (ad esempio, la versione 10.0.15063.0, nota anche come RS2 SDK). Le versioni successive di Windows SDK (in particolare, ShlObj.h e ShlObj_core.h) sono state tuttavia corrette in modo da non generare questo avviso. Quando viene visualizzato questo avviso proveniente dalle azioni di Windows SDK, è possibile eseguire queste azioni:

  1. Passare alla versione più recente di Windows SDK fornita con Visual Studio 2017 versione 15.5.

  2. Disattivare l'avviso prima e dopo l'elemento #include dell'istruzione dell'intestazione di Windows SDK:

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

extern constexpr Collegamento

Nelle versioni precedenti di Visual Studio il compilatore assegna sempre un collegamento interno della variabile constexpr anche quando la variabile è contrassegnata come extern. In Visual Studio 2017 versione 15.5, un nuovo commutatore del compilatore (/Zc:externConstexpr) consente un comportamento corretto e conforme agli standard. Questo diventerà in ultima analisi il comportamento predefinito.

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

Se un file di intestazione contiene una variabile dichiarata extern constexpr, è necessario contrassegnarla come __declspec(selectany) per combinarne correttamente le dichiarazioni duplicate:

extern constexpr __declspec(selectany) int x = 10;

typeid non può essere usato nel tipo di classe incompleto

Nelle versioni precedenti di Visual Studio il compilatore consente erroneamente il codice seguente, che restituisce informazioni sul tipo potenzialmente non corrette. In Visual Studio 2017 versione 15.5 il compilatore genera correttamente un errore:

#include <typeinfo>

struct S;

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

Tipo di destinazione di std::is_convertible

std::is_convertible richiede che il tipo di destinazione sia un tipo restituito valido. Nelle versioni precedenti di Visual Studio il compilatore consente erroneamente i tipi astratti, il che può causare una risoluzione dell'overload non corretta e un comportamento di runtime indesiderato. Il codice seguente ora genera correttamente l'errore 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

Per evitare l'errore, quando si usa is_convertible, è consigliabile confrontare i tipi di puntatore perché un confronto dei tipi non puntatore potrebbe non riuscire se un tipo è astratto:

#include <type_traits>

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

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

Rimozione della specifica di eccezione dinamica e noexcept

In C++17 throw() è un alias per noexcept, throw(<type list>) e throw(...) vengono rimossi e alcuni tipi possono includere noexcept. Questa modifica può causare problemi di compatibilità di origine con il codice conforme a C++14 o versione precedente. L'opzione /Zc:noexceptTypes- può essere usata per ripristinare la versione C++14 di noexcept durante l'uso della modalità C++17 in generale. In questo modo è possibile aggiornare il codice sorgente per garantire la conformità a C++17 senza dover riscrivere contemporaneamente tutto il codice throw().

Il compilatore ora diagnostica anche specifiche di eccezioni più non corrispondenti nelle dichiarazioni in modalità C++17 o con /permissive- il nuovo avviso C5043.

Il codice seguente genera l'errore C5043 e C5040 in Visual Studio 2017 versione 15.5 quando viene applicata l'opzione /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
};

Per rimuovere gli errori durante l'uso /std:c++17di , aggiungere l'opzione alla riga di comando oppure aggiornare il /Zc:noexceptTypes- codice per l'uso noexceptdi , come illustrato nell'esempio seguente:

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

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

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

Variabili inline

I membri dati statici constexpr sono ora implicitamente inline, il che significa che la loro dichiarazione all'interno di una classe è ora la loro definizione. L'uso di una definizione out-of-line per un static constexpr membro dati è ridondante e ora deprecato. In Visual Studio 2017 versione 15.5, quando viene applicato il codice seguente genera ora l'avviso /std:c++17 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

Avviso C4768 relativo a extern "C" __declspec(...) attivo per impostazione predefinita

L'avviso è stato aggiunto in Visual Studio 2017 versione 15.3, ma è disabilitato per impostazione predefinita. In Visual Studio 2017 versione 15.5 l'avviso è abilitato per impostazione predefinita. Per altre informazioni, vedere Nuovo avviso sugli __declspec attributi.

Funzioni impostate come predefinite e __declspec(nothrow)

Il compilatore consentiva la dichiarazione delle funzioni impostate come predefinite con __declspec(nothrow) quando le corrispondenti funzioni di base/membro permettevano le eccezioni. Questo comportamento è contrario allo standard C++ e può causare un comportamento non definito in fase di esecuzione. Lo standard richiede che tali funzioni vengano definite come eliminate se è presente una specifica di eccezione non corrispondente. In /std:c++17, il codice seguente genera 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.
}

Per correggere questo codice, rimuovere __declspec(nothrow) dalla funzione impostata come predefinita o rimuovere = default e fornire una definizione per la funzione con qualsiasi gestione delle eccezioni obbligatorie:

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 e specializzazioni parziali

Con noexcept nel sistema di tipi, le specializzazioni parziali per i tipi "chiamabili" specifici potrebbero non essere compilate o non possono scegliere il modello primario, a causa di una specializzazione parziale mancante per i puntatori a-noexcept-functions.

In questi casi, potrebbe essere necessario aggiungere altre specializzazioni parziali per gestire i noexcept puntatori alle funzioni e noexcept ai puntatori alle funzioni membro. Questi overload sono legali solo in /std:c++17 modalità o successiva. Se è necessario mantenere la compatibilità con le versioni precedenti di C++14 e si sta scrivendo codice che viene usato da altri, è consigliabile proteggere questi nuovi overload in direttive #ifdef. Se si lavora in un modulo autonomo, invece di usare #ifdef le guardie, è sufficiente compilare con il /Zc:noexceptTypes- commutatore.

Il codice seguente viene compilato in /std:c++14 ma ha esito negativo /std:c++17 con l'errore 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>'
}

Il codice seguente ha /std:c++17 esito positivo perché il compilatore sceglie la nuova specializzazione A<void (*)() noexcept>parziale:

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
}

Miglioramenti della conformità nella versione 15.6

C++17 Library Fundamentals V1

P0220R1 incorpora la Library Fundamentals Technical Specification per C++17 nello standard. Vengono illustrati gli aggiornamenti a <experimental/tuple>, <experimental/any><experimental/string_view><experimental/functional><experimental/memory><experimental/optional>, <experimental/memory_resource>e .<experimental/algorithm>

C++17: Miglioramento della deduzione dell'argomento modello di classe per la libreria standard

P0739R0 Spostare adopt_lock_t in primo piano nell'elenco dei parametri per scoped_lock per abilitare un utilizzo coerente di scoped_lock. Consentire al costruttore std::variant di partecipare alla risoluzione dell'overload in più casi per abilitare l'assegnazione delle copie.

Miglioramenti della conformità nella versione 15.7

C++17: Riwording eredita costruttori

P0136R1 specifica che una dichiarazione che assegna un nome a un using costruttore rende ora visibili i costruttori di classi di base corrispondenti alle inizializzazioni della classe derivata, anziché dichiarare costruttori di classi più derivati. Questa ridefinizione è una modifica rispetto a C++14. In Visual Studio 2017 versione 15.7 e versioni successive, in modalità e versioni successive, il codice valido in /std:c++17 C++14 e usa costruttori che ereditano costruttori potrebbero non essere validi o potrebbero avere semantiche diverse.

L'esempio seguente illustra il comportamento di 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).

Nell'esempio seguente viene illustrato /std:c++17 il comportamento in Visual Studio 15.7:

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

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

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

Per altre informazioni, vedere Costruttori.

C++17: Inizializzazione aggregazione estesa

P0017R1

Se il costruttore di una classe di base non è pubblico, ma accessibile a una classe derivata, in /std:c++17 modalità e versioni successive in Visual Studio 2017 versione 15.7, non è più possibile usare parentesi graffe vuote per inizializzare un oggetto del tipo derivato. Nell'esempio seguente viene illustrato il comportamento conforme a C++14:

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

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

In C++17, Derived è ora considerato un tipo di aggregazione. Di conseguenza, l'inizializzazione di Base tramite il costruttore predefinito privato si verifica direttamente come parte della regola di inizializzazione delle aggregazioni estesa. In precedenza, il costruttore privato Base veniva chiamato tramite il costruttore Derived e aveva esito positivo a causa della dichiarazione Friend. Nell'esempio seguente viene illustrato il comportamento di C++17 in Visual Studio versione 15.7 in /std:c++17 modalità:

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: Dichiarazione di parametri di modello non di tipo con auto

P0127R2

In /std:c++17 modalità, il compilatore può ora dedurre il tipo di un argomento modello non di tipo dichiarato con 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

Un effetto di questa nuova funzionalità è che il codice C++14 valido potrebbe non essere valido o potrebbe avere semantiche diverse. Ad esempio, alcuni overload precedentemente non validi sono ora validi. L'esempio seguente illustra il codice C++14 che esegue la compilazione perché la chiamata a example(p) è associata a example(void*);. In Visual Studio 2017 versione 15.7, in /std:c++17 modalità, il example modello di funzione è la corrispondenza migliore.

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
}

Nell'esempio seguente viene illustrato il codice C++17 in modalità Visual Studio 15.7 /std:c++17 :

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: Conversioni di stringhe elementari (parziale)

P0067R5 Funzioni di basso livello indipendenti dalle impostazioni locali per le conversioni tra numeri interi e stringhe e tra numeri a virgola mobile e stringhe.

C++20: evitare il decadimento non necessario (parziale)

P0777R1 Aggiunge una differenziazione tra il concetto di "decadimento" e quello di semplice rimozione dei qualificatori const o reference. In alcuni contesti il nuovo tratto di tipo remove_reference_t sostituisce decay_t. Il supporto per remove_cvref_t è implementato in Visual Studio 2019.

C++17: Algoritmi paralleli

P0024R2 Parallelism TS è incorporato nello standard, con modifiche minori.

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

P0030R1 Aggiunge tre nuovi overload a std::hypot, per i tipi float, doublee , long doubleognuno dei quali include tre parametri di input.

C++17: <filesystem>

P0218R1 Adotta File System TS nello standard con alcune modifiche al testo.

C++17: Funzioni speciali matematiche

P0226R1 Adotta le specifiche tecniche precedenti per Funzioni speciali matematiche nell'intestazione standard <cmath> .

C++17: Guide alla deduzione per la libreria standard

P0433R2 Esegue l'aggiornamento a STL per sfruttare i vantaggi dell'adozione di C++17 di P0091R3 che aggiunge il supporto della deduzione dell'argomento del modello della classe.

C++17: Correzione delle conversioni di stringhe elementari

P0682R1 Spostare le nuove funzioni di conversione delle stringhe elementari da P0067R5 in una nuova intestazione <charconv> e apportare altri miglioramenti, inclusa la modifica della gestione degli errori da usare std::errc anziché std::error_code.

C++17: constexpr per char_traits (parziale)

P0426R1 Modifiche alle std::traits_type funzioni lengthmembro , comparee find per rendere std::string_view utilizzabile nelle espressioni costanti. In Visual Studio 2017 versione 15.6, supportata solo per Clang/LLVM. Nella versione 15.7 il supporto è quasi completo anche per ClXX.

C++17: Argomento predefinito nel modello di classe primaria

Questa modifica del comportamento è una precondizione per P0091R3 - Template argument deduction for class templates.

In precedenza il compilatore ignorava l'argomento predefinito nel modello di classe primario:

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

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

In /std:c++17 modalità in Visual Studio 2017 versione 15.7 l'argomento predefinito non viene ignorato:

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

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

Risoluzione dei nomi dipendenti

Questa modifica del comportamento è una precondizione per P0091R3 - Template argument deduction for class templates.

Nell'esempio seguente il compilatore in Visual Studio 15.6 e versioni precedenti risolve D::type in B<T>::type nel modello di classe primario.

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

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

Visual Studio 2017 versione 15.7, in /std:c++17 modalità, richiede la typename parola chiave nell'istruzione using in D. Senza typename, il compilatore genera l'avviso C4346: e errore C2061: 'B<T*>::type': dependent name is not a typesyntax 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: Attributo [[nodiscard]] - incremento del livello di avviso

In Visual Studio 2017 versione 15.7 in /std:c++17 modalità, il livello di avviso di C4834 viene aumentato da W3 a W1. È possibile disabilitare l'avviso con un cast in voido passando /wd:4834 al compilatore.

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

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

Elenco di inizializzazione della classe di base per un costruttore di modello variadic

Nelle edizioni precedenti di Visual Studio, un elenco di inizializzazione della classe di base per un costruttore di modello variadic con argomenti del modello mancanti è erroneamente consentito senza errori. In Visual Studio 2017 versione 15.7 viene generato un errore del compilatore.

L'esempio di codice seguente in Visual Studio 2017 versione 15.7 produce l'errore 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;

Per correggere l'errore, modificare l'espressione B() in B<T>().

Inizializzazione aggregata di constexpr

Versioni precedenti del compilatore C++ hanno gestito in modo constexpr errato l'inizializzazione aggregazione. Il compilatore ha accettato codice non valido in cui l'aggregazione-init-list aveva troppi elementi e ha prodotto codice oggetto non valido per esso. Il codice seguente è un esempio:

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

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

In Visual Studio 2017 versione 15.7 update 3 e versioni successive, l'esempio precedente genera ora C2078. L'esempio seguente mostra come correggere il codice. Durante l'inizializzazione di una std::array con elenchi di inizializzazione tra parentesi graffe, assegnare alla matrice interna un elenco tra parentesi graffe specifico:

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

Miglioramenti della conformità nella versione 15.8

typename per identificatori non qualificati

In /permissive- modalità, le parole chiave spurie typename sugli identificatori non qualificati nelle definizioni del modello di alias non sono più accettate dal compilatore. Il codice seguente produce ora C7511:

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

Per correggere l'errore, modificare la seconda riga in using X = T;.

__declspec() sul lato destro di definizioni di modelli di alias

__declspec non è più consentito sul lato destro di una definizione di modello alias. In precedenza, il compilatore ha accettato ma ignorato questo codice. Non verrà mai generato un avviso di deprecazione quando è stato usato l'alias.

L'attributo [[deprecated]] C++ standard può essere usato invece e viene rispettato in Visual Studio 2017 versione 15.6. Il codice seguente produce ora C2760:

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

Per correggere l'errore, modificare il codice come segue (con l'attributo posizionato prima del segno '=' della definizione di alias):

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

Diagnostica della ricerca del nome in due fasi

La ricerca del nome in due fasi richiede che i nomi non dipendenti usati nel corpo di modelli siano visibili per il modello in fase di definizione. In precedenza, il compilatore Microsoft C++ non cercava un nome non trovato fino al momento della creazione dell'istanza. È ora necessario che i nomi non dipendenti siano associati al corpo del modello.

Un modo in cui ciò può manifestarsi è con la ricerca in classi di base dipendenti. In precedenza, il compilatore ha consentito l'uso di nomi definiti nelle classi di base dipendenti. È dovuto al fatto che sarebbero stati cercati durante l'istanza quando tutti i tipi vengono risolti. Ora il codice di questo tipo viene considerato un errore. In questi casi è possibile forzare la ricerca della variabile in fase di creazione di un'istanza qualificandola con il tipo della classe di base o rendendola dipendente in altro modo, ad esempio aggiungendo un puntatore this->.

In /permissive- modalità il codice seguente genera ora 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
    }
};

Per correggere l'errore, modificare l'istruzione return in return this->base_value;.

Nota

Nelle versioni della libreria Boost.Python prima della versione 1.70 è stata creata una soluzione alternativa specifica MSVC per una dichiarazione di inoltro del modello in unwind_type.hpp. In /permissive- modalità a partire da Visual Studio 2017 versione 15.8 (_MSC_VER==1915), il compilatore MSVC esegue correttamente la ricerca del nome dipendente dall'argomento (ADL). È ora coerente con altri compilatori, rendendo questa soluzione alternativa non necessaria. Per evitare l'errore C3861: 'unwind_type': identifier not found, aggiornare la libreria Boost.Python.

Dichiarazioni e definizioni con prototipo nello spazio dei nomi std

Lo standard C++ non consente di aggiungere dichiarazioni o definizioni con prototipo nello spazio dei nomi (namespace) std. L'aggiunta di dichiarazioni o definizioni allo spazio dei nomi std o a uno spazio dei nomi all'interno dello spazio dei nomi std ora risulta in un comportamento indefinito.

In futuro, Microsoft modificherà la posizione di definizione di alcuni tipi della libreria standard. Questa modifica causerà l'interruzione del codice esistente che aggiunge dichiarazioni con prototipo allo spazio dei nomi std. Un nuovo avviso, C4643, consente di identificare tali problemi nel codice sorgente. L'avviso è abilitato in /default modalità ed è disattivato per impostazione predefinita. Influisce sui programmi compilati con /Wall o /WX.

Il codice seguente ora attiva l'avviso C4643:

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

Per correggere l'errore, usare una direttiva anziché una #include dichiarazione di inoltro:

#include <vector>

Costruttori con delega a se stessi

Secondo lo standard C++, un compilatore deve generare un messaggio di diagnostica quando un costruttore di delega assegna una delega a se stesso. Il compilatore Microsoft C++ in /std:c++17 e /std:c++latest modalità genera ora C7535.

Senza questo errore, il programma seguente viene compilato ma genera un ciclo infinito:

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

Per evitare il ciclo infinito, delegare un altro costruttore:

class X {
public:

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

offsetof con espressioni costanti

offsetof è stato tradizionalmente implementato usando una macro che richiede un reinterpret_castoggetto . Questo utilizzo non è valido in contesti che richiedono un'espressione costante, ma tradizionalmente il compilatore Microsoft C++ la consentiva. La offsetof macro fornita come parte della libreria standard usa correttamente un intrinseco del compilatore (__builtin_offsetof), ma molti utenti hanno usato il trucco della macro per definire il proprio offsetof.

In Visual Studio 2017 versione 15.8, il compilatore vincola le aree in cui gli operatori reinterpret_cast possono comparire nella modalità predefinita per consentire la conformità del codice al comportamento C++ standard. In /permissive-, i vincoli sono ancora più rigorosi. L'uso del risultato di una offsetof posizione che richiede espressioni costanti può causare l'avviso C4644 o C2975.

Il codice seguente genera C4644 nelle modalità predefinite e e /std:c++17 C2975 in /permissive- modalità:

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

Per correggere l'errore, usare offsetof come definito tramite <cstddef>:

#include <cstddef>

struct Data {
    int x;
};

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

qualificatori cv per le classi di base soggette all'espansione di pacchetto

Le versioni precedenti del compilatore Microsoft C++ non rilevavano che una classe di base aveva qualificatori cv se era anche soggetta all'espansione di pacchetto.

In Visual Studio 2017 versione 15.8, in /permissive- modalità, il codice seguente genera 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
}

Parola chiave template e identificatori di nome annidati

In /permissive- modalità, il compilatore richiede ora la template parola chiave per precedere un nome modello quando si tratta di un identificatore di nome annidato dipendente.

Il codice seguente in /permissive- modalità genera ora 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
    }
};

Per correggere l'errore, aggiungere la parola chiave template all'istruzione Base<T>::example<int>(); , come illustrato nell'esempio seguente:

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

Miglioramenti della conformità nella versione 15.9

Ordine di valutazione da sinistra a destra per gli operatori ->*, [], >> e <<

A partire da C++17, gli operandi degli operatori ->*, [], >> e << devono essere valutati da sinistra a destra. Esistono due casi in cui il compilatore non è in grado di garantire questo ordine:

  • quando una delle espressioni dell'operando è un oggetto passato per valore o contiene un oggetto passato per valore, oppure

  • quando viene compilato usando /clre uno degli operandi è un campo di un oggetto o di un elemento matrice.

Il compilatore genera l'avviso C4866 quando non è possibile garantire la valutazione da sinistra a destra. Il compilatore genera questo avviso solo se /std:c++17 o versioni successive viene specificato, poiché il requisito dell'ordine da sinistra a destra di questi operatori è stato introdotto in C++17.

Per risolvere questo avviso, prima di tutto valutare se è necessaria la valutazione da sinistra a destra degli operandi. Ad esempio, potrebbe essere necessario quando la valutazione degli operandi potrebbe produrre effetti collaterali dipendenti dall'ordine. L'ordine in cui gli operandi vengono valutati non ha alcun effetto osservabile in molti casi. Se l'ordine di valutazione deve essere da sinistra a destra, stabilire se è invece possibile passare gli operandi per riferimento const. Questa modifica consente di eliminare l'avviso nell'esempio di codice seguente:

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

Identificatori nei modelli di membro alias

Un identificatore usato nella definizione del modello di membro alias deve essere dichiarato prima di essere usato.

Nelle versioni precedenti del compilatore è stato consentito il codice seguente. In Visual Studio 2017 versione 15.9, in /permissive- modalità, il compilatore genera 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;

Per correggere l'errore, dichiarare from_template prima di from_template_t.

Modifiche dei moduli

In Visual Studio 2017 versione 15.9 il compilatore genera C5050 ogni volta che le opzioni della riga di comando per i moduli non sono coerenti tra i lati di creazione del modulo e utilizzo del modulo. Nell'esempio seguente sussistono due problemi:

  • Sul lato consumo (main.cpp), l'opzione /EHsc non è specificata.

  • La versione C++ si trova /std:c++17 sul lato creazione e /std:c++14 sul lato consumo.

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

Il compilatore genera C5050 per entrambi questi casi:

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

Il compilatore genera anche C7536 ogni volta che il .ifc file è stato manomesso. L'intestazione dell'interfaccia del modulo contiene un hash SHA2 sotto di essa. In caso di importazione, il .ifc file viene hashato, quindi controllato con l'hash fornito nell'intestazione. Se queste non corrispondono, viene generato l'errore C7536:

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

Ordinamento parziale per alias e contesti non dedotti

È presente una divergenza di implementazione delle regole di ordinamento parziale che interessa gli alias nei contesti non dedotti. Nell'esempio seguente, GCC e il compilatore Microsoft C++ (in /permissive- modalità) generano un errore, mentre Clang accetta il codice.

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

L'esempio precedente genera 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>)'

La divergenza di implementazione è dovuta a una regressione nella formulazione dello standard C++. Per risolvere il problema principale 2235, è stata rimossa parte del testo che avrebbe consentito di ordinare questi overload. Lo standard C++ corrente non offre un meccanismo per un ordinamento parziale di queste funzioni, che quindi vengono considerate ambigue.

Come soluzione alternativa, è consigliabile non affidarsi all'ordinamento parziale per risolvere questo problema. Usare invece SFINAE per rimuovere overload specifici. Nell'esempio seguente viene usata una classe helper IsA per rimuovere il primo overload quando Alloc è una specializzazione di 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;
}

Espressioni non valide o tipi non letterali nelle definizioni di funzioni basate su modelli

Le espressioni non valide e i tipi non letterali ora vengono diagnosticati correttamente nelle definizioni di funzioni basate su modelli specializzate in modo esplicito. In precedenza, tali errori non venivano generati per la definizione della funzione. Tuttavia l'espressione non valida o il tipo non letterale veniva ugualmente diagnosticato se valutato come parte di un'espressione costante.

Nelle versioni precedenti di Visual Studio il codice seguente viene compilato senza avviso:

void g();

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

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

In Visual Studio 2017 versione 15.9 il codice genera l'errore 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'.

Per evitare l'errore, rimuovere il qualificatore constexpr dalla creazione esplicita dell'istanza della funzione f().

Vedi anche

Conformità del linguaggio Microsoft C/C++