Modifications avec rupture dans Visual C++
Lorsque vous effectuez une mise à niveau vers une nouvelle version du compilateur Visual C++, vous pouvez rencontrer des erreurs de compilation et/ou d'exécution dans du code qui pouvait auparavant être compilé et exécuté correctement.Les modifications introduites dans la nouvelle version qui génèrent de tels problèmes sont appelées modifications avec rupture et elles sont généralement requises par des modifications apportées à la norme du langage C++, aux signatures des fonctions ou à la disposition des objets en mémoire.
En Visual C++, bien que la disposition des objets POD (anciennes données brutes) et les interfaces COM soient assurées de ne pas causer de rupture d'une version à une autre, d'autres types de disposition d'objets (par exemple, dans les scénarios qui impliquent un héritage ou une instanciation de modèle) peuvent faire l'objet de modifications.
Pour éviter les erreurs d'exécution qui sont difficiles à détecter et diagnostiquer, nous vous recommandons de ne jamais établir de lien statique à des binaires qui ont été compilés à l'aide de différentes versions du compilateur.En outre, lorsque vous mettez à niveau un projet EXE ou DLL, veillez à mettre à niveau les bibliothèques auxquelles il est lié.Si vous utilisez des types CRT (C Runtime) ou STL (Standard Template Library), ne les transmettez pas entre des binaires (y compris des DLL) qui ont été compilés à l'aide de différentes versions du compilateur.Pour plus d'informations, consultez Erreurs potentielles de passage d'objets CRT entre frontières DLL.
Nous vous recommandons également de ne jamais rédiger de code dépendant d'une disposition particulière pour un objet qui n'est pas une interface COM ou un objet POD.Si vous rédigez un tel code, vous devez vous assurer qu'il fonctionne après la mise à niveau.Pour plus d'informations, consultez Portabilité aux limites ABI (Modern C++).
Le reste de cet article décrit des modifications avec rupture spécifiques dans Visual C++ dans Visual Studio 2013.
Compilateur Visual C++
Le mot clé final génère maintenant une erreur de symbole non résolue, alors que la compilation aurait abouti précédemment :
struct S1 { virtual void f() = 0; }; struct S2 final : public S1 { virtual void f(); }; int main(S2 *p) { p->f(); }
Dans les versions antérieures, aucune erreur n'était émise car l'appel était un appel virtuel ; néanmoins, le programme se bloquait au moment de l'exécution.À présent, une erreur de l'éditeur de liens est émise car la classe est censée être finale.Dans cet exemple, pour corriger l'erreur, vous effectuez une liaison par rapport à l'objet qui contient la définition de S2::f.
Lorsque vous utilisez des fonctions friend dans les espaces de noms, vous devez redéclarer la fonction friend avant de vous y référez ou vous obtiendrez une erreur, parce que le compilateur se conforme maintenant à la norme ISO C++.Par exemple, le code suivant n'est plus compilé :
namespace NS { class C { void func(int); friend void func(C* const) {} }; void C::func(int) { NS::func(this); // error } }
Pour corriger le code, déclarez la fonction 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); } }
La norme C++ n'autorise pas la spécialisation explicite dans une classe.Visual C++ la permet dans certains cas, mais dans des cas tels que l'exemple suivant, une erreur est désormais générée car le compilateur ne considère pas la seconde fonction comme une spécialisation de la première.
template <int N> class S { public: template void f(T& val); template <> void f(char val); }; template class S<1>;
Pour corriger ce code, modifiez la seconde fonction :
template <> void f(char& val);
Visual C++ ne tente plus de supprimer l'ambiguïté des deux fonctions dans l'exemple suivant et émet désormais une erreur :
template<typename T> void Func(T* t = nullptr); template<typename T> void Func(...); int main() { Func<int>(); // error }
Pour corriger ce code, clarifiez l'appel :
template<typename T> void Func(T* t = nullptr); template<typename T> void Func(...); int main() { Func<int>(nullptr); // ok }
Avant que le compilateur ne soit compatible avec ISO C++11, le code suivant doit s'être compilé et avoir fait résoudre x au type int :
auto x = {0}; int y = x;
Ce code résout maintenant x à un type std::initializer_list<int> et provoque une erreur sur la ligne suivante qui tente d'assigner x au type int.(Il n'existe pas de conversion par défaut.) Pour corriger ce code, utilisez int pour remplacer auto:
int x = {0}; int y = x;
L'initialisation d'agrégats n'est plus autorisée lorsque le type de la valeur de droite ne correspond pas au type de la valeur de gauche qui est initialisée, et une erreur est émise car la norme ISO C++11 requiert une initialisation uniforme pour fonctionner sans conversions restrictives.Auparavant, si une conversion restrictive était possible, un avertissement C4242 était émis au lieu d'une erreur.
int i = 0; char c = {i}; // error
Pour corriger le code, ajoutez une conversion restrictive explicite :
int i = 0; char c = {static_cast<char>(i)};
L'initialisation suivante n'est plus autorisée :
void *p = {{0}};
Pour corriger ce code, utilisez l'une de ces formes :
void *p = 0; // or void *p = {0};
La recherche de nom a changé. Le code suivant est résolu différemment dans Visual C++ dans Visual Studio 2012 et Visual C++ dans Visual Studio 2013 :
enum class E1 {a}; enum class E2 {b}; int main() { typedef E2 E1; E1::b; }
Dans Visual C++ dans Visual Studio 2012, l'E1 de l'expression E1::b a été résolue en ::E1 dans la portée globale.Dans Visual C++ dans Visual Studio 2013, l'E1 de l'expression E1::b correspond à la définition typedef E2 dans main() et a le type ::E2.
La disposition des objets a changé. Sur x64, la disposition des objets d'une classe peut changer par rapport aux versions précédentes.Si elle a une fonction virtuelle mais n'a pas de classe de base possédant une fonction virtuelle, le modèle objet du compilateur insère un pointeur vers une table de fonctions virtuelles après la disposition des membres de données.Cela signifie que la disposition peut ne pas être optimale dans tous les cas.Dans les versions précédentes, une optimisation pour x64 tentait d'améliorer la disposition pour vous, mais comme elle ne fonctionnait pas correctement dans les cas complexes de code, elle a été supprimée dans Visual C++ dans Visual Studio 2013.Par exemple, prenons le code suivant :
__declspec(align(16)) struct S1 { }; struct S2 { virtual ~S2(); void *p; S1 s; };
Dans Visual C++ dans Visual Studio 2013, le résultat de sizeof(S2) sur x64 est 48, mais dans les versions antérieures, il équivaut à 32.Pour qu'il soit égal à 32 dans Visual C++ dans Visual Studio 2013 pour x64, ajoutez une classe de base factice possédant une fonction virtuelle :
__declspec(align(16)) struct S1 { }; struct dummy { virtual ~dummy() {} }; struct S2 : public dummy { virtual ~S2(); void *p; S1 s; };
Pour rechercher les emplacements dans votre code qu'une version antérieure aurait essayé d'optimiser, utilisez un compilateur de cette version avec l'option de compilateur /W3 et activez l'avertissement 4370.Par exemple :
#pragma warning(default:4370) __declspec(align(16)) struct S1 { }; struct S2 { virtual ~S2(); void *p; S1 s; };
Sur les compilateurs Visual C++ avant Visual C++ dans Visual Studio 2013, ce code génère le message suivant :
warning C4370: 'S2' : layout of class has changed from a previous version of the compiler due to better packing
Le compilateur x86 a le même problème de disposition non optimale dans toutes les versions de Visual C++.Par exemple, si le code ci-dessous est compilé pour x86 :
struct S { virtual ~S(); int i; double d; };
Le résultat de sizeof(S) est égal à 24.Toutefois, il peut être réduit à 16 si vous utilisez la solution de contournement qui vient d'être mentionnée pour x64 :
struct dummy { virtual ~dummy() {} }; struct S : public dummy { virtual ~S(); int i; double d; };
Bibliothèques Visual C++
Bibliothèque STL (Standard Template Library)
Pour activer les nouvelles optimisations et vérifications de débogage, l'implémentation Visual Studio de la bibliothèque C++ standard interrompt intentionnellement la compatibilité binaire d'une version à la suivante.Par conséquent, lorsque la bibliothèque C++ standard est utilisée, les fichiers objets et les bibliothèques statiques qui sont compilés à l'aide de différentes versions ne peuvent pas être combinés en un seul binaire (EXE ou DLL), et les objets de la bibliothèque C++ standard ne peuvent pas être transmis entre des binaires compilés à l'aide de différentes versions.Une telle combinaison entraîne des erreurs de l'éditeur de liens concernant des incompatibilités _MSC_VER.(_MSC_VER est la macro contenant la version majeure du compilateur. Par exemple, 1800 pour Visual C++ dans Visual Studio 2013.) Cette vérification ne peut pas détecter les combinaisons de DLL et ne peut pas détecter les combinaisons impliquant Visual C++ 2008 ou version antérieure.
Visual C++ dans Visual Studio 2013 détecte des incompatibilités dans _ITERATOR_DEBUG_LEVEL, qui a été implémenté dans Visual C++ 2010, ainsi que des incompatibilités RuntimeLibrary.Elles se produisent lorsque les options de compilateur /MT (version statique), /MTd (débogage statique), /MD (version dynamique) et /MDd (débogage dynamique) sont combinées.Pour plus d’informations, consultez Modifications de dernière minute dans Visual C++ 2012.
Si votre code accepte les modèles d'alias simulés de la version antérieure, vous devez le modifier.Par exemple, au lieu de allocator_traits<A>::rebind_alloc<U>::other, vous devez indiquer allocator_traits<A>::rebind_alloc<U>.Bien que ratio_add<R1, R2>::type ne soit plus nécessaire et qu'il vous soit maintenant recommandé d'indiquer ratio_add<R1, R2>, le code précédent peut encore être compilé car ratio<N, D> doit posséder un typedef « type » pour un taux réduit, qui sera le même type s'il est déjà réduit.
Vous devez utiliser #include <algorithm> lorsque vous appelez std::min() ou std::max().
Si votre code existant utilise les énumérations délimitées simulées de la version antérieure, c'est-à-dire des énumérations délimitées classiques encapsulées dans des espaces de noms, vous devez le modifier.Par exemple, si vous faisiez référence au type std::future_status::future_status, vous devez indiquer std::future_status.Toutefois, la plupart du code reste inchangé. Par exemple, std::future_status::ready est encore compilé.
explicit operator bool() est plus strict que operator unspecified-bool-type().explicit operator bool() permet des conversions explicites vers bool, par exemple, avec shared_ptr<X> sp, static_cast<bool>(sp) et bool b(sp) sont valides ; et des « conversions contextuelles » booléennes testables vers bool, par exemple, if (sp), !sp, sp && whatever.Toutefois, explicit operator bool() interdit les conversions implicites en bool, si bien que vous ne pouvez pas indiquer bool b = sp. De plus, avec un type de retour bool, vous ne pouvez pas indiquer return sp.
Maintenant que de véritables modèles variadiques sont implémentés, _VARIADIC_MAX et les macros connexes n'ont aucun effet. Si vous définissez encore _VARIADIC_MAX, il est ignoré. Si vous avez accepté notre mécanisme de macros destiné à prendre en charge d'une autre façon les modèles variadiques simulés, vous devez modifier votre code.
Outre les mots clés ordinaires, les en-têtes STL interdisent maintenant la transformation en macro des mots clés contextuels « override » et « final ».
reference_wrapper/ref()/cref() interdisent désormais toute liaison aux objets temporaires.
<random> applique désormais strictement ses conditions préalables de compilation.
Différentes caractéristiques de type STL ont la précondition « T sera un type complet ».Bien que le compilateur applique désormais cela plus strictement, il peut ne pas l'appliquer dans toutes les situations.(Comme les violations de condition préalable STL déclenchent un comportement indéfini, le Standard ne garantit pas la mise en vigueur.)
La bibliothèque STL ne prend pas en charge /clr:oldSyntax.
La spécification C++11 pourcommon_type<> a eu des conséquences inattendues et indésirables. En particulier, common_type<int, int>::type a retourné int&&.Par conséquent, Visual C++ implémente la Résolution proposée pour le problème 2141 du groupe de travail de bibliothèque, qui fait que common_type<int, int>::type retourne int.
Effet secondaire de cette modification : le cas d'identité ne s'exécute plus (common_type<T> ne produit pas toujours le type T).Cela est conforme à la résolution proposée, mais interrompt le code qui était fondé sur le comportement précédent.
Si vous avez besoin d'une caractéristique de type d'identité, n'utilisez pas la caractéristique std::identity non standard définie dans <type_traits>, car elle ne fonctionnera pas pour <void>.À la place, implémentez votre propre caractéristique de type d'identité pour répondre à vos besoins.Voici un exemple :
template <typename T> struct Identity { typedef T type; };
MFC et ATL
La Bibliothèque MFC MBCS n'est plus incluse dans Visual Studio, car Unicode est très répandu et l'utilisation de MBCS est considérablement réduite.Cette modification maintient également MFC plus étroitement aligné avec le Kit de développement logiciel (SDK) Windows, car la plupart des nouveaux contrôles et messages sont seulement Unicode.Toutefois, si vous devez continuer à utiliser la bibliothèque MFC MBCS, vous pouvez la télécharger depuis le Centre de téléchargement MSDN.Le package redistribuable Visual C++ inclut toujours cette bibliothèque.
L’accessibilité pour le ruban MFC est modifiée. Au lieu d’une architecture à un niveau, il y a maintenant une architecture hiérarchique. Vous pouvez continuer à utiliser l'ancien comportement en appelant CRibbonBar::EnableSingleLevelAccessibilityMode().
La méthode CDatabase::GetConnect est supprimée. Pour améliorer la sécurité, la chaîne de connexion est maintenant stockée sous forme chiffrée et est déchiffrée uniquement si nécessaire ; elle ne peut pas être retournée sous forme de texte brut. La chaîne peut être obtenue à l’aide de la méthode CDatabase::Dump.
La signature de CWnd::OnPowerBroadcast a changé. La signature de ce gestionnaire de messages est modifiée pour accepter un LPARAM comme deuxième paramètre.
Les signatures sont modifiées pour prendre en compte les gestionnaires de messages. Les listes de paramètres des fonctions suivantes ont été modifiées pour utiliser des gestionnaires de messages ON_WM_* récemment ajoutés :
CWnd::OnDisplayChange remplacé par (UINT, int, int) au lieu de (WPARAM, LPARAM) afin que la nouvelle macro ON_WM_DISPLAYCHANGE puisse être utilisée dans la table des messages.
CFrameWnd::OnDDEInitiate remplacé par (CWnd*, UINT, UNIT) au lieu de (WPARAM, LPARAM) afin que la nouvelle macro ON_WM_DDE_INITIATE puisse être utilisée dans la table des messages.
CFrameWnd::OnDDEExecute remplacé par (CWnd*, HANDLE) au lieu de (WPARAM, LPARAM) afin que la nouvelle macro ON_WM_DDE_EXECUTE puisse être utilisée dans la table des messages.
CFrameWnd::OnDDETerminate replacé par (CWnd*) en tant que paramètre au lieu de (WPARAM, LPARAM) de sorte que la nouvelle macro ON_WM_DDE_TERMINATE puisse être utilisée dans la table des messages.
CMFCMaskedEdit::OnCut remplacé par aucun paramètre au lieu de (WPARAM, LPARAM) afin que la nouvelle macro ON_WM_CUT puisse être utilisée dans la table des messages.
CMFCMaskedEdit::OnClear remplacé par aucun paramètre au lieu de (WPARAM, LPARAM) afin que la nouvelle macro ON_WM_CLEAR puisse être utilisée dans la table des messages.
CMFCMaskedEdit::OnPaste remplacé par aucun paramètre au lieu de (WPARAM, LPARAM) afin que la nouvelle macro ON_WM_PASTE puisse être utilisée dans la table des messages.
Les #ifdef dans les fichiers d’en-tête MFC sont supprimés. De nombreuses #ifdef dans les fichiers d'en-tête MFC liés aux versions de Windows non prises en charge (WINVER < 0x0501) sont supprimées.
DLL ATL (atl120.dll) est supprimée. La bibliothèque ATL est maintenant fournie en tant qu'en-têtes et que bibliothèque statique (atls.lib).
Atlsd.lib, atlsn.lib et atlsnd.lib sont supprimées. Atls.lib n'a plus de dépendances de jeu de caractères ou de code spécifique pour debug/release.Étant donné qu'elle fonctionne de la même manière pour l'Unicode/ANSI et debug/release, une seule version de la bibliothèque est requise.
L'outil ATL/MFC Trace Tool est supprimé avec la DLL ATL, et le mécanisme de traçage est simplifié.Le constructeur CTraceCategory accepte maintenant un paramètre (nom de catégorie), et les macros TRACE appellent les fonctions de création de rapports de débogage CRT.