Ultime modifiche in Visual C++
Quando si esegue l'aggiornamento a una nuova versione del compilatore Visual C++, potrebbero verificarsi errori di compilazione e/o runtime nel codice precedentemente compilato ed eseguito correttamente.Le modifiche apportate alla nuova versione che provocano questi problemi sono note come modifiche di interruzione e in genere sono richieste dalle modifiche nel linguaggio C++ standard, nelle firme di funzione o nel layout degli oggetti in memoria.
In Visual C++, sebbene il layout degli oggetti POD (plain old data, dati non aggiornati) e le interfacce COM non dovrebbero smettere di funzionare da una versione all'altra, altri tipi di layout degli oggetti, ad esempio in scenari che implicano l'ereditarietà o la creazione di istanze di modelli, sono soggetti a modifiche.
Per evitare errori di runtime difficili da rilevare e diagnosticare, è consigliabile non collegarsi mai in modo statico a file binari compilati utilizzando versioni diverse del compilatore.Inoltre, quando si esegue l'aggiornamento a un progetto EXE o DLL, assicurarsi di aggiornare anche le librerie collegate.Se si usano tipi CRT (C Runtime) o STL (Standard Template Library, libreria di modelli standard), non passarli tra file binari (inclusi DLL) compilati utilizzando versioni diverse del compilatore.Per altre informazioni, vedere Potenziali errori di passaggio di oggetti CRT attraverso i limiti DLL.
È inoltre consigliabile non scrivere mai codice che dipende da un layout specifico per un oggetto diverso da un'interfaccia COM o un oggetto POD.Se si scrive codice di questo tipo, è necessario assicurarsi che funzioni dopo l'aggiornamento.Per altre informazioni, vedere Portabilità in base ai limiti ABI (C++ moderno).
Nella parte restante di questo articolo vengono descritte le modifiche di interruzione in Visual C++ in Visual Studio 2013.
Compilatore Visual C++
La parola chiave final genera ora un errore di simbolo non risolto laddove in precedenza sarebbe stato compilato:
struct S1 { virtual void f() = 0; }; struct S2 final : public S1 { virtual void f(); }; int main(S2 *p) { p->f(); }
Nelle versioni precedenti non viene generato alcun errore perché la chiamata è una chiamata virtuale. Tuttavia, il programma viene arrestato in modo anomalo in fase di esecuzione.Ora viene generato un errore del linker perché la classe è finale.In questo esempio, per correggere l'errore, si esegue il collegamento all'oggetto che contiene la definizione di S2::f.
Quando si utilizzano funzioni Friend negli spazi dei nomi, è necessario dichiarare di nuovo la funzione Friend prima di farvi riferimento. In caso contrario, verrà generato un errore perché il compilatore ora è conforme allo standard ISO C++.Nell'esempio seguente la compilazione non viene più eseguita.
namespace NS { class C { void func(int); friend void func(C* const) {} }; void C::func(int) { NS::func(this); // error } }
Per correggere questo codice, dichiarare la funzione friend:
namespace NS { class C { void func(int); friend void func(C* const) {} }; void func(C* const); // conforming fix void C::func(int) { NS::func(this); } }
Il linguaggio C++ standard non consente la specializzazione esplicita in una classe.Sebbene sia consentita da Visual C++ in alcune situazioni, nei casi come quello citato nell'esempio seguente viene generato un errore, poiché il compilatore non considera la seconda funzione come una specializzazione della prima.
template <int N> class S { public: template void f(T& val); template <> void f(char val); }; template class S<1>;
Per correggere questo codice, modificare la seconda funzione:
template <> void f(char& val);
Visual C++ non tenta più di risolvere l'ambiguità delle due funzioni riportate nell'esempio seguente e ora restituisce un errore:
template<typename T> void Func(T* t = nullptr); template<typename T> void Func(...); int main() { Func<int>(); // error }
Per correggere questo codice, definire la chiamata:
template<typename T> void Func(T* t = nullptr); template<typename T> void Func(...); int main() { Func<int>(nullptr); // ok }
Prima che il compilatore fosse reso conforme a C++11 ISO, il codice seguente sarebbe stato compilato e avrebbe generato x per risolversi nel tipo int:
auto x = {0}; int y = x;
Ora il codice risolve x in un tipo di std::initializer_list<int> e genera un errore nella riga successiva che tenta di assegnare x al tipo int.Non viene eseguita alcuna conversione per impostazione predefinita. Per correggere questo codice, utilizzare int per sostituire auto:
int x = {0}; int y = x;
Non è più consentita un'inizializzazione aggregata quando il tipo del valore a destra non corrisponde al tipo del valore a sinistra inizializzato e viene generato un errore poiché lo standard ISO C++11 richiede l'inizializzazione uniforme per funzionare senza conversioni verso un tipo di dati più piccolo.In precedenza, se fosse stata disponibile una conversione verso un tipo di dati più piccolo, sarebbe stato generato un avviso C4242 anziché un errore.
int i = 0; char c = {i}; // error
Per correggere questo codice, aggiungere una conversione esplicita verso un tipo di dati più piccolo:
int i = 0; char c = {static_cast<char>(i)};
La seguente inizializzazione non è più consentita::
void *p = {{0}};
Per correggere questo codice, utilizzare uno di questi formati:
void *p = 0; // or void *p = {0};
È stata modificata la ricerca del nome. Il codice seguente viene risolto in modo diverso in Visual C++ in Visual Studio 2012 e in Visual C++ in Visual Studio 2013:
enum class E1 {a}; enum class E2 {b}; int main() { typedef E2 E1; E1::b; }
In Visual C++ in Visual Studio 2012, E1 nell'espressione E1::b viene risolto in ::E1 nell'ambito globale.In Visual C++ in Visual Studio 2013, E1 nell'espressione E1::b viene risolto nella definizione di typedef E2 in main() e ha il tipo ::E2.
Il layout dell'oggetto è stato modificato. In x64, il layout degli oggetti di una classe può cambiare rispetto alle versioni precedenti.Se dispone di una funzione virtuale, ma non di una classe base con una funzione virtuale, il modello a oggetti del compilatore inserisce un puntatore a una tabella di funzioni virtuali dopo il layout dei membri dati.Ciò significa che il layout potrebbe non essere ottimale in tutti i casi.Nelle versioni precedenti, un'ottimizzazione per x64 tentava di migliorare automaticamente il layout, ma poiché non funzionava correttamente in situazioni di codice complesse, è stata rimossa in Visual C++ in Visual Studio 2013.Si consideri, ad esempio, il seguente codice:
__declspec(align(16)) struct S1 { }; struct S2 { virtual ~S2(); void *p; S1 s; };
In Visual C++ in Visual Studio 2013 il risultato di sizeof(S2) in x64 è 48, ma nelle versioni restituisce 32.Per fare in modo che restituisca 32 in Visual C++ in Visual Studio 2013 per x64, aggiungere una classe base fittizia con una funzione virtuale:
__declspec(align(16)) struct S1 { }; struct dummy { virtual ~dummy() {} }; struct S2 : public dummy { virtual ~S2(); void *p; S1 s; };
Per individuare i punti del codice che una versione precedente avrebbe cercato di ottimizzare, utilizzare un compilatore della suddetta versione insieme all'opzione del compilatore /W3 e attivare l'avviso 4370.Ad esempio:
#pragma warning(default:4370) __declspec(align(16)) struct S1 { }; struct S2 { virtual ~S2(); void *p; S1 s; };
Nei compilatori Visual C++ precedenti a Visual C++ in Visual Studio 2013 il codice genera questo messaggio:
warning C4370: 'S2' : layout of class has changed from a previous version of the compiler due to better packing
Il compilatore x86 ha lo stesso problema di layout inferiore al livello ottimale in tutte le versioni di Visual C++.Ad esempio, se questo codice viene compilato per x86:
struct S { virtual ~S(); int i; double d; };
Il risultato di sizeof(S) è 24.Può tuttavia essere ridotto a 16 se si utilizza la soluzione alternativa indicata per x64:
struct dummy { virtual ~dummy() {} }; struct S : public dummy { virtual ~S(); int i; double d; };
Librerie di Visual C++
Libreria di modelli standard
Per attivare nuove ottimizzazioni e controlli di debug, l'implementazione di Visual Studio della libreria standard C++ interrompe intenzionalmente la compatibilità binaria da una versione a quella successiva.Pertanto, se si usa la libreria standard C++, i file oggetto e le librerie statiche compilati usando versioni diverse non possono essere combinati in un file binario (EXE o DLL) e gli oggetti della libreria standard C++ non possono essere passati tra i file binari compilati usando versioni diverse.Tale combinazione genera errori del linker relativi a mancate corrispondenze di _MSC_VER._MSC_VER è la macro che contiene il numero di versione principale del compilatore, ad esempio 1800 per Visual C++ in Visual Studio 2013. Questo controllo non consente di rilevare la combinazione DLL e altre combinazioni inerenti a Visual C++ 2008 o versioni precedenti.
Visual C++ in Visual Studio 2013 rileva le mancate corrispondenze in _ITERATOR_DEBUG_LEVEL, implementato in Visual C++ 2010, e RuntimeLibrary.Queste si verificano combinando le opzioni del compilatore /MT (versione statica), /MTd (debug statico), /MD (versione dinamica) e /MDd (debug dinamico).Per altre informazioni, vedere Ultime modifiche in Visual C++ 2012.
Se il codice riconosce i modelli simulati dell'alias della versione precedente, è necessario modificarlo.Ad esempio, anziché allocator_traits<A>::rebind_alloc<U>::other, è necessario impostare allocator_traits<A>::rebind_alloc<U>.Anche se ratio_add<R1, R2>::type non è più necessario ed è ora consigliabile impostare ratio_add<R1, R2>, il primo verrà comunque compilato, poiché ratio<N, D> richiede un "tipo" typedef per un report ridotto, che sarà dello stesso tipo se è già ridotto.
È necessario utilizzare #include <algorithm> quando si chiama std::min() o std::max().
Se il codice esistente utilizza le enumerazioni con ambito simulate della versione precedente (enumerazioni tradizionali senza ambito contenute negli spazi dei nomi), è necessario modificarle.Ad esempio, se si fa riferimento al tipo std::future_status::future_status, è ora necessario impostare std::future_status.Tuttavia, la maggior parte del codice non è interessato, ad esempio std::future_status::ready viene compilato.
explicit operator bool() è più rigido di operator unspecified-bool-type().explicit operator bool() consente conversioni esplicite a bool, ad esempio dato shared_ptr<X> sp, static_cast<bool>(sp) e bool b(sp) sono entrambi validi e "conversioni contestuali" verificabili in modo booleano a bool, ad esempio if (sp), !sp, sp && whatever.Tuttavia, explicit operator bool() impedisce le conversioni implicite a bool, pertanto non è possibile impostare bool b = sp e dato un tipo restituito bool non è possibile impostare return sp.
Ora che i modelli variadic reali sono implementati, _VARIADIC_MAX e le macro correlate non hanno alcun effetto. Se si definisce ancora _VARIADIC_MAX, esso viene ignorato. Se si è riconosciuto il sistema di macro con lo scopo di supportare i modelli variadic simulati in qualsiasi altro modo, è necessario modificare il codice.
Oltre alle parole chiave comuni, le intestazioni STL ora impediscono l'impostazione delle macro delle parole chiave sensibili al contesto "override" e "final".
reference_wrapper/ref()/cref() impediscono ora l'associazione a oggetti temporanei.
<random> ora applica rigorosamente le precondizioni in fase di compilazione.
Vari tratti di tipo STL hanno la precondizione "T deve essere un tipo completo".Sebbene il compilatore applichi ora tale regola in modo più rigido, questa non può essere applicata in tutte le situazioni.Poiché le violazioni di precondizione STL attivano un comportamento non definito, lo standard non garantisce l'applicazione.
STL non supporta /clr:oldSyntax.
La specifica C++11 percommon_type<> ha conseguenze impreviste e indesiderate; in particolare fa in modo che common_type<int, int>::type restituisca int&&.Pertanto, Visual C++ implementa la risoluzione proposta per il problema 2141 del gruppo di lavoro della libreria, che fa sì che common_type<int, int>::type restituisca int.
Come effetto collaterale di questa modifica, il case di identità non funziona più (common_type<T> non restituisce sempre il tipo T).Ciò è conforme alla Proposta, ma interrompe il codice basato sul comportamento precedente.
Se è necessario un tratto di tipo identità, non usare l'elemento std::identity non standard definito in <type_traits>, perché non funzionerà con <void>.Diversamente, implementare un tratto di tipo di identità per le proprie esigenze.Di seguito è riportato un esempio:
template <typename T> struct Identity { typedef T type; };
MFC e ATL
Libreria MFC per MBCS non è più inclusa in Visual Studio perché il formato Unicode è estremamente diffuso e l'utilizzo di MBCS si è ridotto significativamente.Questa modifica mantiene inoltre MFC più allineato a Windows SDK stesso, poiché molti dei nuovi controlli e messaggi sono solo Unicode.Tuttavia, se è necessario continuare a usare la libreria MFC per MBCS, è possibile scaricarla dall'Area download di MSDN.Il pacchetto ridistribuibile di Visual C++ include ancora questa libreria.
L'accessibilità per la barra multifunzione MFC è stata modificata. Anziché un'architettura a un livello, è ora un'architettura gerarchica. È ancora possibile utilizzare il comportamento precedente chiamando CRibbonBar::EnableSingleLevelAccessibilityMode().
Il metodo CDatabase::GetConnect è stato rimosso. Per migliorare la sicurezza, la stringa di connessione ora viene archiviata crittografata e verrà decrittografata solo se necessario; non può essere restituita come testo normale. La stringa può essere ottenuta usando il metodo CDatabase::Dump.
La firma di CWnd::OnPowerBroadcast è stata modificata. La firma di questo gestore messaggi è stata modificata per accettare un LPARAM come secondo parametro.
Le firme sono state modificate per includere i gestori di messaggi. Gli elenchi di parametri delle seguenti funzioni sono stati modificati per utilizzare i gestori di messaggi ON_WM_* aggiunti recentemente:
CWnd::OnDisplayChange è stato modificato in (UINT, int, int) anziché (WPARAM, LPARAM) in modo da poter utilizzare la nuova macro ON_WM_DISPLAYCHANGE nella mappa messaggi.
CFrameWnd::OnDDEInitiate è stato modificato in (CWnd*, UINT, UNIT) anziché (WPARAM, LPARAM) in modo da poter utilizzare la nuova macro ON_WM_DDE_INITIATE nella mappa messaggi.
CFrameWnd::OnDDEExecute è stato modificato in (CWnd*, HANDLE) anziché (WPARAM, LPARAM) in modo da poter utilizzare la nuova macro ON_WM_DDE_EXECUTE nella mappa messaggi.
CFrameWnd::OnDDETerminate è stato modificato in (CWnd*) come parametro anziché (WPARAM, LPARAM) in modo da poter utilizzare la nuova macro ON_WM_DDE_TERMINATE nella mappa messaggi.
CMFCMaskedEdit::OnCut è stato modificato per non richiedere alcun parametro anziché (WPARAM, LPARAM) in modo da poter utilizzare la nuova macro ON_WM_CUT nella mappa messaggi.
CMFCMaskedEdit::OnClear è stato modificato per non richiedere alcun parametro anziché (WPARAM, LPARAM) in modo da poter utilizzare la nuova macro ON_WM_CLEAR nella mappa messaggi.
CMFCMaskedEdit::OnPaste è stato modificato per non richiedere alcun parametro anziché (WPARAM, LPARAM) in modo da poter utilizzare la nuova macro ON_WM_PASTE nella mappa messaggi.
Gli oggetti #ifdef nei file di intestazione MFC sono stati rimossi. Numerosi oggetti #ifdef nei file di intestazione MFC relativi alle versioni di Windows non supportate (WINVER < 0x0501) sono stati rimossi.
La DLL ATL (atl120.dll) è stata rimossa. La DLL ATL viene ora fornita come intestazioni e come libreria statica (atls.lib).
Atlsd.lib, atlsn.lib e atlsnd.lib sono stati rimossi. Atls.lib non include più dipendenze o codice con set di caratteri specifici per il debug/rilascio.Poiché funziona esattamente come per Unicode/ANSI e per il debug/rilascio, è richiesta una sola versione della libreria.
Lo strumento di traccia ATL/MFC è stato rimosso contestualmente alla DLL ATL e il meccanismo di traccia è stato semplificato.Il costruttore CTraceCategory ora accetta un parametro (il nome della categoria) e le macro TRACE chiamano funzioni CRT di report di debug.