Miglioramenti della conformità C++, modifiche del 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 riportato di seguito in questo articolo.
Questo documento elenca le modifiche apportate a Visual Studio 2017. Per una guida alle modifiche in Visual Studio 2022, vedere Miglioramenti della conformità di 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 Novità di Visual C++ da 2003 a 2015.
Miglioramenti della conformità in Visual Studio 2017 RTW (versione 15.0)
Con il supporto per l'inizializzazione generalizzata constexpr
e non statica dei membri dati (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. Per lo stato corrente del compilatore, vedere Conformità del linguaggio Microsoft C/C++.
C++11: Supporto di espressione SFINAE in più librerie
Il compilatore continua a migliorare il supporto per l'espressione SFINAE. È necessario per la deduzione e la sostituzione dell'argomento 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 con un costruttore fornito dall'utente, nessun membro dati non statico privato o protetto, nessuna classe di base e nessuna funzione virtuale. A partire da C++14, le aggregazioni possono contenere inizializzatori di 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 autorizzate a contenere determinati tipi di dichiarazioni, istruzioni if e switch, istruzioni loop e mutazioni di oggetti la cui durata è iniziata all'interno della valutazione dell'espressione constexpr
. Non esiste più un requisito per cui una constexpr
funzione membro non statica deve essere implicitamente const
. Per altre informazioni, vedere Relaxing constraints on functions .For more information, see Relaxing constraints on constexpr
functions.
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 per il compilatore che è previsto il comportamento di fall-through. L'attributo impedisce al compilatore di emettere avvisi in tali casi. Per ulteriori informazioni, vedere P0188R0 - Wording for [[fallthrough]] attribute
.
Cicli generalizzati basati su for
intervalli
I cicli basati su for
intervalli non richiedono più che begin()
gli oggetti restituiti end()
e dello stesso tipo. Questa modifica consente di end()
restituire un sentinel usato dagli intervalli in range-v3
e dalla specifica tecnica degli intervalli completata ma non abbastanza pubblicata. Per ulteriori 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.7p1
In , in copy-list-initialization
, il compilatore deve considerare un costruttore esplicito per la risoluzione dell'overload. Tuttavia, deve generare un errore se questo particolare overload 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 erroneamente trattato l'inizializzazione copy-list-initialization allo stesso modo dell'inizializzazione normale 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 sinistro di un'operazione di valutazione condizionale non è valido 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, printf
ad esempio , devono essere facilmente copiabili. Quando tali oggetti vengono passati, il compilatore crea 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
, l'oggetto fornito operator LPCTSTR()
deve essere usato 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 si prevede che la sostituzione non riesca a causa dell'inaccessibilità di un nome. Potrebbe aver causato un arresto anomalo o un comportamento imprevisto in fase di esecuzione, perché il compilatore ha erroneamente chiamato 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 del modello mancanti. Non si noterebbe quando il modello mancante è apparso in un elenco di parametri del modello: ad esempio, quando parte di un argomento modello predefinito o un parametro di modello non di tipo manca. 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. Pertanto, se nell'argomento decltype
viene trovata una specializzazione non dipendente, non viene posticipata fino a quando non viene creata un'istanza. 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 sono stati corretti i bug che richiedevano la soluzione alternativa. 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
espressioni 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 Restituzione di 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 sono di tipo narrowing. Converte dal tipo sottostante di un'enumerazione con ambito all'enumerazione stessa. La conversione è disponibile quando la relativa 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 ulteriori informazioni, vedere P0018R3 - Lambda Capture of *this by Value as [=,*this]
.
Rimozione di operator++
per bool
operator++
non è più supportato nei tipi bool
. Per ulteriori 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 ulteriori informazioni, vedere P0001R1 - Remove Deprecated Use of the register Keyword
.
Chiamate a modelli di membro eliminati
Nelle versioni precedenti di Visual Studio, in alcuni casi il compilatore non genera un errore per le chiamate in formato non corretto a un modello di membro eliminato. Queste chiamate potrebbero causare arresti anomali in fase di esecuzione. Il codice seguente genera 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 rileva questo errore in fase di compilazione: un oggetto nativo con ctor di copia eliminato viene passato tra un limite nativo e gestito per 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 l'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 di Windows più recenti hanno l'attributo rimosso da tutti i tipi forniti. Se si usa un SDK meno recente, è necessario eliminare questi avvisi per tutte le chiamate ai tipi spediti.
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 out-of-line di una funzione membro del modello non 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 prvalue di tipo puntatore 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 genera ora C2243 quando visualizza 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 genererà un avviso in /permissive
e 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 l'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, l'uso di offsetof(T, m)
dove m è un "designatore membro composto" genera un avviso quando si esegue la compilazione 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 l'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 __declspec
gli attributi
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 /Wall
opzioni e /WX
, il codice seguente genera l'avviso C4768:
__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 emette 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 è attivato per impostazione predefinita nel livello di avviso W2.
std::is_convertible
per i tipi matrice
Le versioni precedenti del compilatore hanno dato risultati non corretti per i std::is_convertible
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 ignoravano se un distruttore era privato quando decideva 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. In questo caso, le versioni precedenti del compilatore hanno sempre eseguito una ricerca dipendente dall'argomento. Potrebbe causare un comportamento imprevisto del runtime, 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 in cui sono dichiarati, non nell'ordine in cui vengono visualizzati 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 fornito un constexpr
collegamento interno variabile, anche quando la variabile è stata contrassegnata come extern
. In Visual Studio 2017 versione 15.5, un nuovo commutatore del compilatore, /Zc:externConstexpr
, abilita il 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 delle specifiche delle eccezioni dinamiche e noexcept
.
not_fn()
not_fn
P0005R4 è una sostituzione di not1
e not2
.
Riformulazione di enable_shared_from_this
enable_shared_from_this
P0033R1 è 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 dei nodi dai contenitori associativi (ad esempio, map
, set
, unordered_map
, unordered_set
) che possono quindi essere modificati e nuovamente inseriti 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 di classe std::function
aveva diversi costruttori che accettavano 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 viene impostata l'opzione /std:c++17
del compilatore o versione successiva, quasi tutte le funzionalità della libreria standard nell'allegato D vengono contrassegnate come deprecate. Per altre informazioni, vedere Le funzionalità della libreria standard in Annex D vengono contrassegnate come deprecate.
Lo std::tr2::sys
spazio dei nomi in <experimental/filesystem>
ora genera un avviso di deprecazione in per /std:c++14
impostazione predefinita e ora viene rimosso in /std:c++17
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. Gli aggiornamenti includono l'aggiunta di nel sistema dei noexcept
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 in modo univoco 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 /std:c++17
del compilatore successiva, quasi tutte le funzionalità della libreria standard nell'allegato 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 più 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, venivano generati solo sotto l'opzione 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 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 è 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:
Passare alla versione più recente di Windows SDK fornita con Visual Studio 2017 versione 15.5.
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
) abilita il 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 dinamica delle specifiche delle eccezioni 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 consente anche di diagnosticare più specifiche di eccezioni 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 mentre si usa /std:c++17
ancora , aggiungere l'opzione /Zc:noexceptTypes-
alla riga di comando oppure aggiornare il codice per usare noexcept
, 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 relativa dichiarazione all'interno di una classe è ora la relativa 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 l'opzione viene applicata, il /std:c++17
codice seguente genera ora l'avviso 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 l'errore 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 dei tipi, le specializzazioni parziali per i tipi "chiamabili" specifici potrebbero non essere compilate o non riescono a 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 di funzione e noexcept
i puntatori alle funzioni membro. Questi overload sono validi 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 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 di <experimental/tuple>
, <experimental/functional>
<experimental/optional>
, <experimental/any>
, <experimental/string_view>
, <experimental/memory>
, , <experimental/memory_resource>
e <experimental/algorithm>
.
C++17: Miglioramento della deduzione degli argomenti del 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: Riformulazione dell'ereditarietà dei costruttori
P0136R1 specifica che una using
dichiarazione che assegna un nome a un costruttore rende ora visibili i costruttori di classi base corrispondenti alle inizializzazioni della classe derivata, anziché dichiarare più costruttori di classi derivate. Questa ridefinizione è una modifica rispetto a C++14. In Visual Studio 2017 versione 15.7 e successive, in /std:c++17
modalità e versioni successive, il codice valido in C++14 e usa costruttori che ereditano potrebbero non essere validi o 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).
L'esempio seguente mostra /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 estesa dell'aggregazione
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.
L'esempio seguente mostra 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.
L'esempio seguente mostra 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
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 avere semantica diversa. 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
}
L'esempio seguente mostra il codice C++17 in Visual Studio 15.7 in /std:c++17
modalità :
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
, double
e long double
, ognuno dei quali ha 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: Ripristino delle conversioni di stringhe elementari
P0682R1 Spostare le nuove funzioni di conversione di stringhe elementari da P0067R5 in una nuova intestazione <charconv>
e apportare altri miglioramenti, inclusa la modifica della gestione degli errori da usare std::errc
invece di std::error_code
.
C++17: constexpr
per char_traits
(parziale)
P0426R1 Modifiche alle funzioni length
, compare
e find
del membro std::traits_type
per rendere utilizzabile std::string_view
nelle espressioni costanti. In Visual Studio 2017 versione 15.6, supportato 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: 'B<T*>::type': dependent name is not a type
e l'errore 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: 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 a void
o 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
Le versioni precedenti del compilatore C++ hanno gestito constexpr
erroneamente l'inizializzazione aggregata. Il compilatore ha accettato codice non valido in cui l'oggetto aggregate-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 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 di modello di alias non vengono 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 di alias. In precedenza, il compilatore ha accettato ma ignorato questo codice. Non genera mai un avviso di deprecazione quando è stato usato l'alias.
L'attributo [[deprecated]]
C++ standard può essere invece usato ed è rispettato in Visual Studio 2017 versione 15.6. Il codice seguente genera 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 consentiva l'uso di nomi definiti nelle classi di base dipendenti. È dovuto al fatto che sarebbero stati cercati durante la creazione di istanze 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 precedenti alla 1.70 è stata creata una soluzione alternativa specifica di 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. Influirà 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 #include
direttiva anziché una dichiarazione forward:
#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
le 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 oggetto reinterpret_cast
. 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 molte persone hanno usato il trucco 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ù rigidi. L'uso del risultato di un oggetto offsetof
in posizioni che richiedono espressioni costanti può comportare il codice che genera l'avviso C4644 o C2975.
Il codice seguente genera L4644 nelle modalità predefinite e /std:c++17
e 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 l'errore 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 ora richiede che la template
parola chiave preceda un nome modello quando si tratta di un nested-name-specifier 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
/clr
e uno degli operandi è un campo di un oggetto o di un elemento di 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, in quanto il requisito dell'ordine da sinistra a destra di questi operatori è stato introdotto in C++17.
Per risolvere questo avviso, valutare innanzitutto 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 L5050 ogni volta che le opzioni della riga di comando per i moduli non sono coerenti tra i lati di creazione del modulo e consumo di moduli. 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 di 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 l'errore C5050 per entrambi i casi seguenti:
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 fase di importazione, il .ifc
file viene sottoposto a hashing, quindi controllato rispetto all'hash fornito nell'intestazione. Se 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 l'errore 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()
.