Partager via


Améliorations de la conformité, changements de comportement et correctifs de bogues de C++ dans Visual Studio 2017

Microsoft C/C++ dans Visual Studio (MSVC) apporte des améliorations de la conformité et des correctifs de bogues dans chaque version. Cet article répertorie les améliorations apportées par version majeure, puis par version. Pour aller directement aux modifications apportées à une version spécifique, utilisez la liste ci-dessous dans cet article.

Ce document répertorie les modifications apportées à Visual Studio 2017. Pour obtenir un guide des modifications apportées à Visual Studio 2022, consultez Améliorations de la conformité de C++ dans Visual Studio 2022. Pour obtenir un guide des modifications apportées à Visual Studio 2019, consultez Améliorations de la conformité de C++ dans Visual Studio 2019. Pour obtenir la liste complète des améliorations de la conformité précédentes, consultez Nouveautés de Visual C++ entre 2003 et 2015.

Améliorations de la conformité dans Visual Studio 2017 RTW (version 15.0)

Avec la prise en charge des expressions constexpr généralisées et de l’initialisation de données membres non statiques (NSDMI) pour les agrégats, le compilateur MSVC dans Visual Studio 2017 est désormais complet pour les fonctionnalités ajoutées à la norme C++ 14. Cependant, le compilateur ne dispose pas encore de certaines fonctionnalités des normes C++11 et C++98. Pour connaître l’état actuel du compilateur, consultez Conformité du langage Microsoft C/C++.

C++11 : Prise en charge de la fonctionnalité SFINAE pour les expressions dans d’autres bibliothèques

Le compilateur Visual C++ continue d’améliorer sa prise en charge de la fonctionnalité SFINAE pour les expressions. Celle-ci est nécessaire pour la déduction et la substitution d’argument de modèle dans lesquelles les expressions decltype et constexpr peuvent apparaître en tant que paramètres de modèle. Pour plus d’informations, consultez Expression SFINAE improvements in Visual Studio 2017 RC.

C++14 : NSDMI pour les agrégats

Un agrégat est un tableau ou une classe sans constructeur fourni par l’utilisateur, sans membres de données non statiques privés ou protégés, sans classes de base et sans fonctions virtuelles. À compter de C++14, les agrégats peuvent contenir des initialiseurs de membres. Pour plus d’informations, consultez Member initializers and aggregates.

C++14 : Extension de constexpr

Les expressions déclarées en tant qu’expressions constexpr sont désormais autorisées à contenir certains types de déclarations, des instructions if et switch, des instructions de boucle et une mutation d’objets dont la vie a commencé dans l’évaluation de l’expression constexpr. Il n’est plus nécessaire qu’une fonction membre non statique constexpr soit implicitement de type const. Pour plus d’informations, consultez Relaxing constraints on constexpr functions.

C++17 : Terse static_assert

Le paramètre de message pour static_assert est facultatif. Pour plus d’informations, consultez N3928: Extending static_assert, v2.

C++17 : Attribut [[fallthrough]]

En mode /std:c++17 et ultérieur, l’attribut [[fallthrough]] est utilisable dans le contexte des instructions switch en tant qu’indicateur informant le compilateur que le comportement fallthrough est prévu. Cet attribut empêche le compilateur d’émettre des avertissements dans de tels cas. Pour plus d’informations, consultez P0188R0 - Wording for [[fallthrough]] attribute.

Généralisation de boucles for basées sur une plage

Les boucles end() basées sur une plage ne nécessitent plus que for et begin() retournent des objets du même type. Avec ce changement, end() peut retourner un objet sentinel, à l’image de ceux utilisés par les plages définies dans range-v3 et la spécification technique d’autres plages disponibles mais pas encore publiées. Pour plus d’informations, consultez P0184R0 - Generalizing the Range-Based for Loop.

Copy-list-initialization

Visual Studio 2017 lève correctement des erreurs de compilateur liées à la création d’objets à l’aide de listes d’initialiseurs. Ces erreurs n’étaient pas prises en compte dans Visual Studio 2015, et pouvaient entraîner des incidents ou un comportement d’exécution non défini. Conformément à N4594 13.3.1.7p1, dans copy-list-initialization, le compilateur est tenu de prendre en compte un constructeur explicite pour la résolution de surcharge. Toutefois, il doit lever une erreur si cette surcharge particulière est choisie.

Les deux exemples suivants se compilent dans Visual Studio 2015, mais pas dans 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 &'

}

Pour corriger l’erreur, utilisez une initialisation directe :

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

Dans Visual Studio 2015, le compilateur traitait à tort copy-list-initialization de la même façon que l’instruction copy-initialization ordinaire ; il envisageait uniquement de convertir les constructeurs pour résoudre la surcharge. Dans l’exemple suivant, Visual Studio 2015 choisit MyInt(23). Visual Studio 2017 lève correctement l’erreur.

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

Cet exemple est semblable au précédent, mais génère une erreur différente. Il réussit dans Visual Studio 2015, mais échoue dans Visual Studio 2017 avec l’erreur 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
}

Typedefs dépréciées

Visual Studio 2017 émet désormais l’avertissement correct pour les typedefs dépréciées qui sont déclarées dans une classe ou un struct. Dans Visual Studio 2015, l’exemple suivant se compile sans avertissement. Il produit C4996 dans 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 génère correctement une erreur quand l’opérande de gauche d’une opération à évaluation conditionnelle n’est pas valide dans un contexte constexpr. Le code suivant se compile dans Visual Studio 2015, mais pas dans Visual Studio 2017 où il lève l’erreur 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
}

Pour corriger cette erreur, déclarez la fonction array::size() en tant que constexpr ou supprimez le qualificateur constexpr de f.

Types de classe transmis aux fonctions variadiques

Dans Visual Studio 2017, les classes ou structures qui sont passées à une fonction variadique telle que printf doivent être copiables de manière triviale. Quand de tels objets sont passés, le compilateur effectue simplement une copie au niveau du bit et n’appelle pas le constructeur ou le destructeur.

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

Pour corriger cette erreur, vous pouvez appeler une fonction membre qui retourne un type copiable de manière triviale,

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

ou utiliser un cast statique pour convertir l’objet avant de le transmettre :

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

Pour les chaînes générées et gérées à l’aide de CString, vous devez utiliser le operator LPCTSTR() fourni pour caster un objet CString vers le pointeur C attendu par la chaîne de format.

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

Qualificateurs cv dans la construction de la classe

Dans Visual Studio 2015, le compilateur ignore parfois à tort le qualificateur cv pendant la génération d’un objet de classe par le biais d’un appel de constructeur. Ce problème peut provoquer un incident ou un comportement inattendu au moment de l’exécution. L’exemple suivant se compile dans Visual Studio 2015, mais génère une erreur de compilateur dans Visual Studio 2017 :

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

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

Pour corriger l’erreur, déclarez operator int() en tant que const.

Contrôle d’accès sur les noms qualifiés dans les modèles

Les versions précédentes du compilateur n’effectuaient pas de contrôle d’accès sur les noms qualifiés dans certains contextes de modèle. Ce problème peut interférer avec le comportement attendu de la fonctionnalité SFINAE où la substitution est supposée échouer en raison de l’inaccessibilité d’un nom. Cette situation peut entraîner un incident ou un comportement inattendu au moment de l’exécution en raison de l’appel par le compilateur de la surcharge incorrecte de l’opérateur. Dans Visual Studio 2017, une erreur de compilateur est générée. L’erreur spécifique peut varier, mais une erreur classique est C2672, « aucune fonction surchargée correspondante trouvée ». Le code suivant se compile dans Visual Studio 2015, mais génère une erreur dans 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.
}

Listes d’arguments de modèle manquantes

Dans Visual Studio 2015 et versions antérieures, le compilateur ne diagnostiquait pas toutes les listes d’arguments de modèle manquantes. Il ne remarquait pas lorsque le modèle manquant apparaissait dans une liste de paramètres de modèle : par exemple, lorsqu’une partie d’un argument de modèle par défaut ou un paramètre de modèle sans type était manquant. Ce problème peut entraîner un comportement imprévisible, y compris des pannes du compilateur ou un comportement inattendu au moment de l’exécution. Le code suivant se compile dans Visual Studio 2015, mais génère une erreur dans 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;

Fonctionnalité SFINAE pour les expressions

Pour prendre en charge la fonctionnalité SFINAE pour les expressions, le compilateur analyse maintenant les arguments decltype quand les modèles sont déclarés et non instanciés. Par conséquent, si une spécialisation non dépendante est trouvée dans l’argument decltype, elle n’est pas différée à l’instanciation. Elle est traitée immédiatement, et toute erreur résultante est diagnostiquée à ce moment-là.

L’exemple suivant montre une erreur de compilateur de ce type générée au moment de la déclaration :

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

Classes déclarées dans des espaces de noms anonymes

Selon la norme C++, une classe déclarée dans un espace de noms anonyme a une liaison interne et, par conséquent, ne peut pas être exportée. Dans Visual Studio 2015 et versions antérieures, cette règle n’était pas appliquée. Dans Visual Studio 2017, la règle est partiellement appliquée. Dans Visual Studio 2017, l’exemple suivant lève l’erreur C2201:

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

Initialiseurs par défaut pour les membres de classe value (C++/CLI)

Dans Visual Studio 2015 et antérieur, le compilateur autorisait (mais ignorait) un initialiseur de membre par défaut pour un membre d’une classe value. L’initialisation par défaut d’une classe value initialise systématiquement les membres à zéro. Un constructeur par défaut n’est pas autorisé. Dans Visual Studio 2017, les initialiseurs de membres par défaut déclenchent une erreur de compilateur, comme illustré dans cet exemple :

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

Indexeurs par défaut (C++/CLI)

Dans Visual Studio 2015 et antérieur, le compilateur identifiait à tort une propriété par défaut en tant qu’indexeur par défaut dans certaines circonstances. Il était possible de contourner le problème en utilisant l’identificateur default pour accéder à la propriété. La solution de contournement elle-même est devenue problématique dès que default a été introduit comme mot clé dans C++11. Dans Visual Studio 2017, les bogues qui nécessitaient la solution de contournement ont été corrigés. Le compilateur lève désormais une erreur quand default est utilisé pour accéder à la propriété par défaut pour une 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
}

Dans Visual Studio 2017, vous pouvez accéder aux deux propriétés Value par leur nom :

#using "class1.dll"

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

Améliorations de la conformité dans la version 15.3

constexpr lambda

Les expressions lambda peuvent désormais être utilisées dans les expressions constantes. Pour plus d’informations, consultez Expressions constexpr lambda en C++.

if constexpr dans les modèles de fonctions

Un modèle de fonction peut contenir des instructions if constexpr permettant de créer une branche au moment de la compilation. Pour plus d’informations, consultez Instructions if constexpr.

Instructions Selection avec initialiseurs

Une instruction if peut inclure un initialiseur qui présente une variable à portée de bloc dans l’instruction elle-même. Pour plus d’informations, consultez Instructions if avec initialiseur.

Attributs [[maybe_unused]] et [[nodiscard]]

Le nouvel attribut [[maybe_unused]] réduit au silence les avertissements lorsqu’une entité n’est pas utilisée. L’attribut [[nodiscard]] crée un avertissement si la valeur de retour d’un appel de fonction est ignorée. Pour plus d’informations, consultez Attributes in C++.

Utilisation des espaces de noms d’attribut sans répétition

Nouvelle syntaxe pour activer uniquement un seul identificateur d’espace de noms dans une liste d’attributs. Pour plus d’informations, consultez Attributes in C++.

Liaisons structurées

Il est désormais possible dans une déclaration unique de stocker une valeur avec des noms individuels pour ses composants, lorsque la valeur est un tableau, un std::tuple ou un std::pair, ou contient uniquement des membres de données non statiques publiques. Pour plus d’informations, consultez P0144R0 - Structured Bindings et Retour de plusieurs valeurs à partir d’une fonction.

Règles de construction pour les valeurs enum class

Il existe maintenant une conversion implicite pour les énumérations délimitées qui ne sont pas restrictives. Elle convertit le type sous-jacent d’une énumération délimitée en l’énumération elle-même. La conversion est disponible lorsque sa définition n’introduit pas d’énumérateur et quand la source utilise une syntaxe d’initialisation de liste. Pour plus d’informations, consultez P0138R2 - Construction Rules for enum class Values et Énumérations.

Capture de *this par valeur

L’objet *this dans une expression lambda peut désormais être capturé par sa valeur. Ce changement permet des scénarios dans lesquels l’expression lambda est invoquée dans des opérations parallèles et asynchrones, en particulier sur des architectures de machines plus récentes. Pour plus d’informations, consultez P0018R3 - Lambda Capture of *this by Value as [=,*this].

Suppression de operator++ pour bool

operator++ n’est plus pris en charge sur les types bool. Pour plus d’informations, consultez P0002R1 - Remove Deprecated operator++(bool).

Suppression du mot clé register déconseillé

Le mot clé register, déjà déprécié (et ignoré par le compilateur), est maintenant supprimé du langage. Pour plus d’informations, consultez P0001R1 - Remove Deprecated Use of the register Keyword.

Appels à des modèles membres supprimés

Dans les versions précédentes de Visual Studio, le compilateur ne parvenait pas dans certains cas à émettre une erreur pour les appels incorrects à un modèle membre supprimé. Ces appels entraînent potentiellement des incidents au moment de l’exécution. Le code suivant génère désormais l’erreur 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
}

Pour corriger cette erreur, déclarez i comme int.

Vérifications de conditions préalables pour les traits de type

Visual Studio 2017 version 15.3 améliore les vérifications de conditions préalables pour les traits de type afin de suivre plus strictement la norme. Une telle vérification doit être affectée. Le code suivant génère l’erreur C2139 dans Visual Studio 2017 version 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

Nouvel avertissement du compilateur et nouvelles vérifications à l’exécution sur du marshaling de fonctions natives à managées

L’appel des fonctions managées aux fonctions natives nécessite un marshaling. Le CLR effectue le marshaling, mais il ne comprend pas la sémantique C++. Si vous passez un objet natif par valeur, le CLR appelle le constructeur de copie de l’objet ou utilise BitBlt, ce qui peut provoquer un comportement non défini lors de l’exécution.

Désormais, le compilateur émet un avertissement s’il trouve cette erreur au moment de la compilation : un objet natif avec un ctor de copie supprimé est passé entre une limite native et gérée par valeur. Pour les cas où le compilateur ne sait rien au moment de la compilation, il injecte une vérification à l’exécution afin que le programme appelle std::terminate immédiatement dès qu’un marshaling incorrect se produit. Dans Visual Studio 2017 version 15.3, le code suivant génère l’erreur 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.`
}

Pour corriger cette erreur, supprimez la directive #pragma managed pour marquer l’appelant comme natif et éviter le marshaling.

Avertissement d’API expérimentale pour WinRT

Les API WinRT publiées pour l’expérimentation et les commentaires sont décorées avec Windows.Foundation.Metadata.ExperimentalAttribute. Dans Visual Studio 2017 version 15.3, le compilateur génère l’avertissement C4698 pour cet attribut. Certaines API dans les versions précédentes du SDK Windows ont déjà été décorées avec l’attribut et les appels à ces API déclenchent cet avertissement du compilateur. Les SDK Windows plus récents ont l’attribut supprimé de tous les types livrés. Si vous utilisez un SDK plus ancien, vous devez supprimer ces avertissements pour tous les appels à des types livrés.

Le code suivant génère l’avertissement 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

Pour désactiver l’avertissement, ajoutez un #pragma :

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

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

#pragma warning(pop)

Définition hors ligne d’une fonction membre de modèle

Visual Studio 2017 version 15.3 génère une erreur pour une définition hors ligne d’une fonction membre de modèle qui n’a pas été déclarée dans la classe. Le code suivant génère désormais l’erreur C2039 :

struct S {};

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

Pour corriger cette erreur, ajoutez une déclaration à la classe :

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

Tentative de prendre l’adresse du pointeur this

En C++, this est une prvalue de pointeur de type vers X. Vous ne pouvez pas prendre l’adresse de this ni la lier à une référence lvalue. Dans les versions précédentes de Visual Studio, le compilateur vous permettait de contourner cette restriction en utilisant un cast. Dans Visual Studio 2017 version 15.3, le compilateur génère l’erreur C2664.

Conversion vers une classe de base inaccessible

Visual Studio 2017 version 15.3 génère une erreur quand vous essayez de convertir un type en une classe de base qui n’est pas accessible. Le code suivant est incorrect et peut éventuellement provoquer un incident lors de l’exécution. Le compilateur génère désormais l’erreur C2243 quand il rencontre un code tel que le suivant :

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

Les arguments par défaut ne sont pas autorisés sur les définitions hors ligne des fonctions membres

Les arguments par défaut ne sont pas autorisés sur les définitions hors ligne des fonctions membres dans les classes de modèles. Le compilateur émet un avertissement sous /permissive et une erreur matérielle sous /permissive-.

Dans les versions précédentes de Visual Studio, le code incorrect suivant peut entraîner un incident lors de l’exécution. Visual Studio 2017 version 15.3 génère l’avertissement 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
{
    // ...
}

Pour corriger cette erreur, supprimez l’argument par défaut = false.

Utilisation de offsetof avec désignateur de membre composé

Dans Visual Studio 2017 version 15.3, l’utilisation de offsetof(T, m)m est un « désignateur de membre composé » aboutit à un avertissement quand vous compilez avec l’option /Wall. Le code suivant est incorrect et peut éventuellement provoquer un incident lors de l’exécution. Visual Studio 2017 version 15.3 génère l’avertissement 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]);

Pour corriger le code, désactivez l’avertissement avec un pragma ou modifiez le code pour ne pas utiliser offsetof :

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

Utilisation de offsetof avec des données membres static ou une fonction membre

Dans Visual Studio 2017 version 15.3, l’utilisation de offsetof(T, m)m fait référence à des données membres static ou une fonction membre aboutit à une erreur. Le code suivant génère une erreur 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'

Ce code est incorrect et peut éventuellement provoquer un incident lors de l’exécution. Pour corriger cette erreur, modifiez le code pour ne plus appeler un comportement non défini. Il s’agit de code non portable qui n’est pas autorisé par la norme C++.

Nouvel avertissement sur les attributs __declspec

Dans Visual Studio 2017 version 15.3, le compilateur n’ignore plus les attributs si __declspec(...) est appliqué avant une spécification de liaison extern "C" externe. Avant, le compilateur ignorait l’attribut, ce qui pouvait avoir des implications lors de l’exécution. Lorsque les options et /WX les /Wall sont définies, le code suivant génère l’avertissement C4768 :

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

Pour résoudre l’avertissement, placez extern "C" d’abord :

extern "C" __declspec(noinline) HRESULT __stdcall

Cet avertissement est désactivé par défaut dans Visual Studio 2017 version 15.3, et affecte uniquement le code compilé avec /Wall/WX. Depuis Visual Studio 2017 version 15.5, il est activé par défaut en tant qu’avertissement de niveau 3.

decltype et appels à des destructeurs supprimés

Dans les versions précédentes de Visual Studio, le compilateur ne détectait pas quand un appel à un destructeur supprimé se produisait dans le contexte de l’expression associée à decltype. Dans Visual Studio 2017 version 15.3, le code suivant génère l’erreur 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
}

Variables const non initialisées

La version Visual Studio 2017 RTW avait une régression : le compilateur C++ n’émettait pas de diagnostic pour une variable const non initialisée. Cette régression a été corrigée dans Visual Studio 2017 version 15.3. Le code suivant génère désormais « l’avertissement C4132 :

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

Pour corriger cette erreur, attribuez une valeur à Value.

Déclarations vides

Visual Studio 2017 version 15.3 émet désormais un avertissement relatif aux déclarations vides pour tous les types, non seulement les types intégrés. Le code suivant génère désormais un avertissement C4091 de niveau 2 pour les quatre déclarations :

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

Pour supprimer les avertissements, commentez ou supprimez les déclarations vides. Dans les cas où l’objet sans nom doit avoir un effet secondaire (par exemple, RAII), vous devez lui affecter un nom.

L’avertissement est exclu sous /Wv:18 et est activé par défaut sous le niveau d’avertissement W2.

std::is_convertible pour les types de tableaux

Les versions précédentes du compilateur ont donné des résultats incorrects avec std::is_convertible pour les types tableau. Cela obligeait les auteurs de bibliothèques à particulariser le compilateur Microsoft C++ lors de l’utilisation d’une caractéristique de type std::is_convertible<...>. Dans l’exemple suivant, les assertions statiques passent dans les versions antérieures de Visual Studio, mais échouent dans Visual Studio 2017 version 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> est calculé en vérifiant si une définition de fonction imaginaire est correcte :

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

Destructeurs privés et std::is_constructible

Les versions précédentes du compilateur ignoraient si un destructeur était privé lors de la détermination du résultat de std::is_constructible. Il en tient compte désormais. Dans l’exemple suivant, les assertions statiques passent dans les versions antérieures de Visual Studio, mais échouent dans Visual Studio 2017 version 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);

Les destructeurs privés entraînent la non-constructibilité d’un type. std::is_constructible<T, Args...> est calculé comme si la déclaration suivante était écrite :

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

Cet appel implique un appel de destructeur.

C2668 : Résolution de surcharge ambiguë

Parfois, les versions précédentes du compilateur ne parvenaient pas à détecter une ambiguïté lors de la découverte de plusieurs candidats par le biais simultanément des déclarations et des recherches dépendantes d’arguments. Cet échec pouvait conduire à choisir une surcharge incorrecte et entraîner un comportement inattendu au moment de l’exécution. Dans l’exemple suivant, Visual Studio 2017 version 15.3 lève correctement l’erreur 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
}

Pour corriger le code, supprimez l’instruction using N::f si vous souhaitez appeler ::f().

C2660 : Déclarations de fonction locale et recherche dépendante d’argument

Les déclarations de fonction locale masquent la déclaration de fonction dans la portée englobante et désactivent la recherche dépendante d’argument. Les versions précédentes du compilateur effectuaient toujours une recherche dépendant d’un argument dans ce cas. Cela pouvait entraîner un comportement de runtime inattendu si le compilateur choisissait la surcharge incorrecte. En règle générale, l’erreur est causée par une signature incorrecte de la déclaration de fonction locale. Dans l’exemple suivant, Visual Studio 2017 version 15.3 lève correctement l’erreur 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);
}

Pour résoudre le problème, changez la signature f(S) ou supprimez-la.

C5038 : Ordre d’initialisation dans les listes d’initialiseurs

Les membres de classe sont initialisés dans l’ordre suivant lequel ils sont déclarés, et non selon celui de leur apparition dans les listes d’initialiseurs. Les versions précédentes du compilateur n’avertissaient pas lorsque l’ordre de la liste d’initialiseurs était différent de celui des déclarations. Ce problème pouvait causer un comportement d’exécution non défini si l’initialisation d’un membre dépendait d’un autre membre dans la liste déjà en cours d’initialisation. Dans l’exemple suivant, Visual Studio 2017 version 15.3 (avec /Wall) lève un avertissement 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;
};

Pour résoudre le problème, réorganisez la liste d’initialiseurs afin d’avoir le même ordre que dans les déclarations. Un avertissement similaire est déclenché lorsqu’un ou les deux initialiseurs se réfèrent aux membres de classe de base.

Cet avertissement est désactivé par défaut et affecte uniquement le code compilé avec /Wall.

Améliorations de la conformité dans la version 15.5

Les fonctionnalités marquées avec [14] sont disponibles sans conditions, même en mode /std:c++14.

Nouveau commutateur de compilateur pour extern constexpr

Dans les versions antérieures de Visual Studio, le compilateur retournait toujours une liaison interne pour la variable constexpr, même quand la variable était marquée comme extern. Dans Visual Studio 2017 version 15.5, un nouveau commutateur de compilateur, /Zc:externConstexpr, active un comportement correct et conforme aux normes. Pour plus d’informations, consultez Liaison extern constexpr.

Suppression des spécifications d’exceptions dynamiques

P0003R5Les spécifications d’exceptions dynamiques ont été dépréciées dans C++11. La fonctionnalité est supprimée dans C++17, mais la spécification (toujours) dépréciée throw() est conservée uniquement comme alias pour noexcept(true). Pour plus d’informations, consultez Suppression des spécifications d’exceptions dynamiques et noexcept.

not_fn()

P0005R4not_fn vient remplacer not1 et not2.

Reformulation de enable_shared_from_this

P0033R1enable_shared_from_this a été ajouté dans C++11. La norme C++17 met à jour la spécification pour mieux gérer certains cas extrêmes. [14]

Ajout de mappages et d’ensembles

P0083R3 Cette fonctionnalité permet d’extraire des nœuds de conteneurs associatifs (par exemple map, set, unordered_map, unordered_set) pour les modifier, puis de les réinsérer dans le même conteneur ou dans un autre conteneur qui utilise le même type de nœud. (Un cas d’usage courant consiste à extraire un nœud d’un std::map, à changer la clé et à réinsérer le nœud.)

Composants de bibliothèque rudimentaires dépréciés

P0174R2 Plusieurs fonctionnalités de la bibliothèque standard C++ ont été remplacées par de nouvelles fonctionnalités au fil des années, ou ont été jugées inutiles ou problématiques. Ces fonctionnalités sont officiellement dépréciées dans C++17.

Suppression de la prise en charge d’un allocateur dans std::function

P0302R1 Dans les versions antérieures à C++17, le modèle de classe std::function avait plusieurs constructeurs avec un argument allocateur. Toutefois, l’utilisation d’allocateurs dans ce contexte était problématique et la sémantique n’était pas claire. Les constructeurs problématiques ont été supprimés.

Corrections pour not_fn()

P0358R1 La nouvelle formulation pour std::not_fn permet de prendre en charge la propagation de la catégorie de valeur quand un wrapper est appelé.

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

P0414R2 Fusion des modifications apportées à shared_ptr dans C++17 dans Library Fundamentals. [14]

Correction de shared_ptr pour les tableaux

P0497R0 Correctifs apportés à la prise en charge de shared_ptr pour les tableaux. [14]

Clarification de insert_return_type

P0508R0 Les conteneurs associatifs ou non ordonnés avec des clés uniques ont une fonction membre insert qui retourne un type imbriqué insert_return_type. Le type de retour est maintenant défini comme une spécialisation d’un type qui est paramétré sur les éléments Iterator et NodeType du conteneur.

Variables inline pour la bibliothèque standard

Pour P0607R0, plusieurs variables courantes déclarées dans la bibliothèque standard sont désormais déclarées inline.

Fonctionnalités dépréciées de l’annexe D

L’annexe D de la norme C++ contient toutes les fonctionnalités qui ont été dépréciées, notamment shared_ptr::unique(), <codecvt> et namespace std::tr1. Quand l’option de compilateur en mode /std:c++17 ou ultérieur est définie, presque toutes les fonctionnalités de la bibliothèque standard listées dans l’annexe D sont marquées comme dépréciées. Pour plus d’informations, consultez Les fonctionnalités de la bibliothèque standard répertoriées dans l’annexe D sont marquées comme dépréciées.

Désormais, l’espace de noms std::tr2::sys dans <experimental/filesystem> émet un avertissement de dépréciation sous /std:c++14 par défaut, et est supprimé sous /std:c++17 et versions ultérieures par défaut.

La conformité est améliorée dans <iostream>, car l’utilisation d’une extension non standard (spécialisations de classe explicites) n’est plus nécessaire.

La bibliothèque standard utilise désormais les modèles de variable en interne.

La bibliothèque standard a été mise à jour en réponse aux modifications apportées au compilateur C++17. Les mises à jour incluent l’ajout de noexcept dans le système de type et la suppression de spécifications d’exception dynamique.

Changement de classement partiel

Le compilateur rejette maintenant le code suivant et génère le message d’erreur approprié :

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

Dans l’exemple ci-dessus, le problème provient de l’utilisation de fonctions de deux types différents (const/non-const et pack/non-pack). Pour éviter l’erreur du compilateur, utilisez des fonctions d’un même type. Ensuite, le compilateur peut ordonner sans ambiguïté les fonctions.

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

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

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

Gestionnaires d’exceptions

Les gestionnaires de référence au type tableau ou fonction ne sont plus mis en correspondance pour les objets exception. Le compilateur applique maintenant cette règle correctement et déclenche un avertissement de niveau 4 C4843. De plus, il ne met plus en correspondance un gestionnaire de char* ou wchar_t* avec un littéral de chaîne quand /Zc:strictStrings est utilisé.

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

Le code suivant permet d’éviter cette erreur :

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

std::tr1 L’espace de noms est déprécié

L’espace de noms std::tr1 non standard est désormais marqué comme déprécié dans les deux modes C++14 et C++17. Dans Visual Studio 2017 version 15.5, le code suivant génère l’erreur 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.

Pour corriger cette erreur, supprimez la référence à l’espace de noms 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;
}

Les fonctionnalités de la bibliothèque standard dans l’annexe D sont marquées comme dépréciées

Quand le compilateur en mode /std:c++17 ou ultérieur est défini, presque toutes les fonctionnalités de la bibliothèque standard listées dans l’annexe D sont marquées comme dépréciées.

Dans Visual Studio 2017 version 15.5, le code suivant génère l’erreur 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.

Pour corriger cette erreur, suivez les instructions données dans le texte d’avertissement, comme illustré dans le code suivant :

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

Variables locales non référencées

Dans Visual Studio 15.5, l’avertissement C4189 est affiché dans plus de cas, comme indiqué dans le code suivant :

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

Pour corriger cette erreur, supprimez la variable inutilisée.

Commentaires à ligne unique

Dans Visual Studio 2017 version 15.5, les avertissements C4001 et C4179 ne sont plus générés par le compilateur C. Auparavant, ils étaient uniquement générés quand le commutateur du compilateur /Za était défini. Ces avertissements ne sont plus utiles, car les commentaires sur une seule ligne sont intégrés à la norme C depuis la version 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'

Quand le code n’a pas besoin d’offrir une compatibilité descendante, évitez l’avertissement en supprimant les avertissements C4001 et C4179. Si le code doit offrir une compatibilité descendante, supprimez uniquement C4619.

/* C only */

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

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

Attributs __declspec avec une liaison extern "C"

Dans les versions antérieures de Visual Studio, le compilateur ignorait les attributs __declspec(...) quand __declspec(...) était appliqué avant la spécification de la liaison extern "C". Ce comportement provoquait la génération de code de façon inattendue pour l’utilisateur, avec un impact possible sur l’exécution. L’avertissement C4768, ajouté dans Visual Studio 2017 version 15.3, était désactivé par défaut. Dans Visual Studio 2017 version 15.5, l’avertissement est activé par défaut.

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

Pour corriger cette erreur, ajoutez la spécification de liaison avant l’attribut __declspec :

extern "C" __declspec(noinline) HRESULT __stdcall

Le nouvel avertissement C4768 ci-dessous est généré sur certains en-têtes Windows SDK fournis avec Visual Studio 2017 version 15.3 ou antérieure (par exemple, dans la version 10.0.15063.0, appelée RS2 SDK). Les versions ultérieures des en-têtes Windows SDK (en particulier, ShlObj.h et ShlObj_core.h) ont été corrigées pour ne plus générer cet avertissement. Si vous voyez cet avertissement généré par les en-têtes Windows SDK, effectuez les actions suivantes :

  1. Installez le dernier Windows SDK, fourni avec Visual Studio 2017 version 15.5.

  2. Désactivez l’avertissement pour l’élément #include de l’instruction d’en-tête Windows SDK :

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

Liaison extern constexpr

Dans les versions antérieures de Visual Studio, le compilateur retournait toujours une liaison interne pour la variable constexpr, même quand la variable était marquée comme extern. Dans Visual Studio 2017 version 15.5, un nouveau commutateur de compilateur (/Zc:externConstexpr) active un comportement correct et conforme aux normes. Cela deviendra le comportement par défaut.

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

Si un fichier d’en-tête contient une variable déclarée extern constexpr, la variable doit être marquée comme __declspec(selectany) pour permettre la combinaison correcte de ses déclarations dupliquées :

extern constexpr __declspec(selectany) int x = 10;

typeid ne peut pas être utilisé sur un type de classe incomplet

Dans les versions antérieures de Visual Studio, le compilateur autorisait à tort le code suivant, ce qui pouvait produire des informations de type incorrectes. Dans Visual Studio 2017 version 15.5, le compilateur génère une erreur :

#include <typeinfo>

struct S;

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

Type cible std::is_convertible

std::is_convertible exige que le type de cible soit un type de retour valide. Dans les versions antérieures de Visual Studio, le compilateur autorisait à tort les types abstraits, ce qui pouvait entraîner une résolution de surcharge incorrecte et un comportement inattendu au moment de l’exécution. Le code suivant génère désormais correctement l’erreur 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

Pour éviter cette erreur, quand vous utilisez is_convertible, vous devez comparer les types de pointeur, car une comparaison de type non pointeur peut échouer si un type est abstrait :

#include <type_traits>

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

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

Suppression des spécifications d’exceptions dynamiques et noexcept

Dans C++17, throw() est un alias de noexcept, les exceptions throw(<type list>) et throw(...) sont supprimées, et certains types peuvent inclure noexcept. Ces changements entraînent parfois des problèmes de compatibilité avec le code conforme à C++14 ou une version antérieure. Vous pouvez utiliser le commutateur /Zc:noexceptTypes- pour rétablir la version C++14 de noexcept si vous utilisez généralement le mode C++17. Cela vous permet de mettre à jour votre code source pour le rendre conforme à C++17 sans pour autant avoir à réécrire tout votre code throw().

Avec le nouvel avertissement C5043, le compilateur diagnostique également maintenant davantage de spécifications d’exceptions incompatibles dans les déclarations en mode C++17 ou avec /permissive-.

Le code suivant génère les avertissements C5043 et C5040 dans Visual Studio 2017 version 15.5 quand le commutateur /std:c++17 est défini :

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

Pour éviter ces erreurs quand vous utilisez /std:c++17, ajoutez le commutateur /Zc:noexceptTypes- dans la ligne de commande ou bien mettez à jour votre code pour utiliser noexcept, comme illustré dans l’exemple suivant :

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

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

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

Variables inline

Les membres de données constexpr sont désormais implicitement inline, ce qui signifie que leur déclaration dans une classe correspond maintenant à leur définition. L’utilisation d’une définition hors ligne pour un membre de données static constexpr est redondante et désormais dépréciée. Dans Visual Studio 2017 version 15.5, lorsque le commutateur /std:c++17 est appliqué, le code suivant génère désormais un avertissement 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

L’avertissement C4768 extern "C" __declspec(...) est maintenant activé par défaut

L’avertissement, ajouté dans Visual Studio 2017 version 15.3, était désactivé par défaut. Dans Visual Studio 2017 version 15.5, l’avertissement est activé par défaut. Pour plus d’informations, consultez Nouvel avertissement sur les attributs __declspec.

Fonctions utilisées par défaut et __declspec(nothrow)

Auparavant, le compilateur autorisait la déclaration de fonctions par défaut à l’aide de __declspec(nothrow) quand les fonctions de base/membres correspondantes autorisaient les exceptions. Ce comportement non conforme à la norme C++ peut entraîner un comportement inattendu au moment de l’exécution. La norme exige que ces fonctions soient marquées comme supprimées en cas de non-correspondance d’une spécification d’exception. Sous /std:c++17, le code suivant lève l’erreur 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.
}

Pour corriger ce code, supprimez __declspec (nothrow) dans la fonction par défaut, ou supprimez = default et ajoutez une définition de la fonction et la gestion d’exceptions nécessaire :

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 et spécialisations partielles

Quand noexcept est utilisé dans le système de type, les spécialisations partielles pour la correspondance de certains types « pouvant être appelés » peuvent ne pas compiler, ou échouer à choisir le modèle principal en raison d’une spécialisation partielle manquante pour les pointeurs de fonctions noexcept.

Dans ce cas de figure, vous devrez peut-être ajouter d’autres spécialisations partielles pour gérer les pointeurs de fonctions noexcept ainsi que les pointeurs noexcept de fonctions membres. Ces surcharges sont uniquement autorisées en mode /std:c++17 ou ultérieur. Si vous devez garantir la compatibilité descendante avec C++14 et que votre code est destiné à être utilisé par d’autres personnes, vous devez protéger ces nouvelles surcharges à l’intérieur de directives #ifdef. Si votre code fait partie d’un module autonome, au lieu d’utiliser des protections #ifdef, vous pouvez simplement compiler le code avec le commutateur /Zc:noexceptTypes-.

Le code suivant se compile sous /std:c++14 mais échoue avec /std:c++17 l’erreur 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>'
}

Le code suivant se compile correctement en mode /std:c++17, car le compilateur choisit la nouvelle spécialisation partielle A<void (*)() noexcept> :

template <typename T> struct A;

template <>
struct A<void(*)()>
{
    static const bool value = true;
};

template <>
struct A<void(*)() noexcept>
{
    static const bool value = true;
};

template <typename T>
bool g(T t)
{
    return A<T>::value;
}

void f() noexcept {}

int main()
{
    return g(&f) ? 0 : 1; // OK
}

Améliorations de la conformité dans la version 15.6

C++17 - Library Fundamentals V1

P0220R1 incorpore la spécification technique relative aux notions de base des bibliothèques (Library Fundamentals TS) pour C++17 à la norme. Couvre les mises à jour de <experimental/tuple>, <experimental/optional>, <experimental/functional>, <experimental/any>, <experimental/string_view>, <experimental/memory>, <experimental/memory_resource> et <experimental/algorithm>.

C++17 : Amélioration de la déduction d’arguments de modèle de classe pour la bibliothèque standard

P0739R0adopt_lock_t est déplacé au début de la liste des paramètres de scoped_lock pour garantir une utilisation cohérente de scoped_lock. Le constructeur std::variant est autorisé à participer à la résolution de surcharge dans davantage de cas pour permettre l’assignation de copie.

Améliorations de la conformité dans la version 15.7

C++17 : Reformulation de l’héritage des constructeurs

P0136R1 Dans une déclaration using qui nomme un constructeur, les initialisations de la classe dérivée peuvent désormais voir les constructeurs de la classe de base correspondante, évitant ainsi la déclaration de constructeurs supplémentaires pour la classe dérivée. Cette reformulation est un changement à compter de C++14. Dans Visual Studio 2017 versions 15.7 et ultérieures, en mode /std:c++17 et ultérieur, le code qui utilise l’héritage de constructeurs et qui est valide dans C++14 peut être non valide ou avoir une sémantique différente.

L’exemple suivant montre le comportement de 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’exemple suivant montre le comportement de /std:c++17 dans 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)

Pour plus d’informations, consultez Constructeurs.

C++17 : Initialisation d’agrégats étendue

P0017R1

Si le constructeur d’une classe de base est non public, mais qu’il est accessible à une classe dérivée, en mode /std:c++17 et ultérieur dans Visual Studio 2017 version 15.7, vous ne pouvez plus utiliser d’accolades vides pour initialiser un objet du type dérivé. L’exemple suivant montre le comportement conforme à 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.

Dans C++17, Derived est désormais considéré comme un type d’agrégat. Cela signifie que l’initialisation de Base par le biais du constructeur par défaut privé se produit donc directement dans le cadre de la règle d’initialisation d’agrégats étendue. Auparavant, le constructeur privé Base était appelé par le biais du constructeur Derived, ce qui réussissait en raison de la déclaration friend. L’exemple suivant montre le comportement de C++17 dans Visual Studio version 15.7 en mode /std:c++17 :

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 : Déclaration de paramètres de modèle sans type avec auto

P0127R2

En mode /std:c++17, le compilateur peut désormais déduire le type d’un argument de modèle sans type déclaré avec 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

L’une des conséquences de cette nouvelle fonctionnalité est que le code valide dans C++14 peut être non valide ou avoir une sémantique différente. Par exemple, certaines surcharges qui étaient précédemment non valides sont à présent valides. L’exemple suivant montre du code C++14 dont la compilation réussit car l’appel à example(p) est lié à example(void*);. Dans Visual Studio 2017 version 15.7, en mode /std:c++17, le modèle de fonction example offre la meilleure correspondance.

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’exemple suivant montre du code C++17 dans Visual Studio 15.7 en mode /std:c++17 :

template <int N> struct A;
template <typename T, T N> int example(A<N>*);

void example(void *);

void sample(A<0> *p)
{
    example(p); // C2280: 'int example<int,0>(A<0>*)': attempting to reference a deleted function
}

C++17 : Conversions de chaîne élémentaires (état partiel)

P0067R5 Fonctions de bas niveau indépendantes des paramètres régionaux pour les conversions entre entiers et chaînes et entre nombres à virgule flottante et chaînes.

C++20 : Prévention des dégradations inutiles (état partiel)

P0777R1 Ajoute une distinction entre le concept de « dégradation » et la simple opération consistant à supprimer const ou des qualificateurs de référence. Le nouveau trait de type remove_reference_t remplace decay_t dans certains contextes. La prise en charge de remove_cvref_t est implémentée dans Visual Studio 2019.

C++17 : Algorithmes parallèles

P0024R2 La spécification technique relative au parallélisme (Parallelism TS) est intégrée à la norme, avec des modifications mineures.

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

P0030R1 Ajoute trois nouvelles surcharges à std::hypot pour les types float, double et long double, chacun d’eux ayant trois paramètres d’entrée.

C++17 : <filesystem>

P0218R1 Intègre la spécification technique relative au système de fichiers (File System TS) à la norme, avec quelques modifications de formulation.

C++17 : Fonctions mathématiques spéciales

P0226R1 Intègre les spécifications techniques précédentes pour les fonctions mathématiques spéciales à l’en-tête <cmath> standard.

C++17 : Guides de déduction pour la bibliothèque standard

P0433R2 Met à jour STL pour tirer parti de l’adoption par C++17 de P0091R3, qui ajoute la prise en charge de la déduction d’arguments de modèle de classe.

C++17 : Réparation de conversions de chaînes élémentaires

P0682R1 Déplace les nouvelles fonctions de conversion de chaîne élémentaire de P0067R5 vers un nouvel en-tête <charconv> et apporte d’autres améliorations, notamment l’utilisation de std::errc pour la gestion des erreurs au lieu de std::error_code.

C++17 : constexpr pour char_traits (partiel)

P0426R1 Changements apportés aux fonctions membres std::traits_typelength, compare et find pour rendre std::string_view utilisable dans les expressions constantes. (Dans Visual Studio 2017 version 15.6, pris en charge uniquement pour Clang/LLVM. Dans la version 15.7, la prise en charge est presque complète pour ClXX également.)

C++17 : Argument par défaut dans le modèle de classe primaire

Ce changement de comportement est une condition préalable pour P0091R3 - Template argument deduction for class templates.

Auparavant, le compilateur ignorait l’argument par défaut dans le modèle de classe primaire :

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

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

Dans Visual Studio 2017 version 15.7, en mode /std:c++17, l’argument par défaut n’est pas ignoré :

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

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

Résolution de noms indépendante

Ce changement de comportement est une condition préalable pour P0091R3 - Template argument deduction for class templates.

Dans l’exemple suivant, le compilateur dans Visual Studio 15.6 et antérieur résout D::type en B<T>::type dans le modèle de classe primaire.

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

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

Visual Studio 2017 version 15.7, en mode /std:c++17, nécessite le mot clé typename dans l’instruction using en D. Sans typename, le compilateur lève l’avertissement C4346 'B<T*>::type': dependent name is not a type et l’erreur 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 - Attribut [[nodiscard]] - élévation du niveau d’avertissement

Dans Visual Studio 2017 version 15.7, en /std:c++17 mode, le niveau d’avertissement de C4834 est passé de W3 à W1. Vous pouvez désactiver l’avertissement en effectuant un cast en void ou en passant /wd:4834 au compilateur.

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

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

Liste d’initialisation de classe de base du constructeur de modèle variadique

Dans les éditions précédentes de Visual Studio, une liste d’initialisation de classe de base du constructeur de modèle variadique dont des arguments de modèle étaient manquants était autorisée par erreur sans signaler d’erreur. Dans Visual Studio 2017 version 15.7, une erreur de compilateur est générée.

L’exemple de code suivant dans Visual Studio 2017 version 15.7 déclenche l’erreur 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;

Pour corriger l’erreur, modifiez l’expression B() en B<T>().

Initialisation d'agrégats constexpr

Les versions précédentes du compilateur C++ géraient mal l’initialisation d’agrégats constexpr. Le compilateur acceptait du code non valide dans lequel la liste d’initialisation d’agrégats comptait trop d’éléments, et produisait un code d’objet incorrect pour celle-ci. Le code suivant en est un exemple :

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

Dans Visual Studio 2017 version 15.7 mise à jour 3 et ultérieures, l’exemple précédent lève désormais l’erreur C2078. L’exemple de code suivant montre corriger le code. Lors de l’initialisation d’un std::array avec nested brace-init-lists, attribuez au tableau interne son propre braced-list :

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

Améliorations de la conformité dans la version 15.8

typename sur les identificateurs non qualifiés

En mode /permissive-, les mots clés typename parasites sur des identificateurs non qualifiés dans les définitions de modèle d’alias ne sont plus acceptés par le compilateur. Le code suivant génère désormais l’erreur C7511 :

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

Pour corriger cette erreur, remplacez la deuxième ligne par using X = T;.

__declspec() dans la partie droite des définitions de modèle d’alias

__declspec n’est plus autorisé dans la partie droite d’une définition de modèle d’alias. Auparavant, le compilateur acceptait mais ignorait ce code. Il n’entraînait jamais d’avertissement de dépréciation lorsque l’alias était utilisé.

Vous pouvez utiliser l’attribut C++ standard [[deprecated]] à la place. Celui-ci est pris en compte dans Visual Studio 2017 version 15.6. Le code suivant génère désormais l’erreur C2760 :

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

Pour corriger cette erreur, remplacez le code par ce qui suit (attribut placé avant le signe '=' de la définition d’alias) :

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

Diagnostics de la recherche de nom en deux phases

La recherche de nom en deux phases nécessite que les noms non dépendants utilisés dans les corps de modèle soient visibles par le modèle au moment de la définition. Avant, le compilateur Microsoft C++ ne procédait pas à la recherche d’un nom introuvable avant l’instanciation. Il exige désormais que les noms non dépendants soient liés dans le corps du modèle.

Cela peut notamment se manifester par une recherche dans les classes de base dépendantes. Auparavant, le compilateur autorisait l’utilisation de noms définis dans des classes de base dépendantes. C’est parce qu’ils étaient recherchés au moment de l’instanciation quand tous les types sont résolus. Ce code est désormais traité comme une erreur. Dans ces cas, vous pouvez forcer la recherche de la variable au moment de l’instanciation en la qualifiant avec le type de classe de base ou en la rendant dépendante, par exemple en ajoutant un pointeur this->.

En mode /permissive-, le code suivant lève désormais l’erreur 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
    }
};

Pour corriger cette erreur, remplacez l’instruction return par return this->base_value;.

Notes

Dans les versions de la bibliothèque Boost.Python antérieures à la version 1.70, il existe une solution de contournement spécifique de MSVC pour une déclaration anticipée de modèle dans unwind_type.hpp. En mode /permissive- à partir de Visual Studio 2017 version 15.8 (_MSC_VER==1915), le compilateur MSVC effectue correctement une recherche de nom dépendant d’un argument (ADL). Il est désormais cohérent avec d’autres compilateurs, ce qui rend cette solution de contournement inutile. Pour éviter l’erreur C3861 : 'unwind_type': identifier not found, mettez à jour votre bibliothèque Boost.Python.

définitions et déclarations anticipées dans l’espace de noms std

La norme C++ ne permet pas à un utilisateur d’ajouter des définitions ou des déclarations anticipées dans l’espace de noms std. L’ajout de définitions ou de déclarations à l’espace de noms std ou à un espace de noms dans l’espace de noms std donne désormais lieu à un comportement non défini.

Il est prévu que Microsoft affecte la définition de certains types de bibliothèque standard à un autre emplacement. Ce changement entraînera l’arrêt de tout code existant qui ajoute des déclarations anticipées à l’espace de noms std. Un nouvel avertissement, C4643, vous aide à identifier de tels problèmes liés la source. Cet avertissement est activé en mode /default et est désactivé par défaut. Il affecte les programmes compilés avec /Wall ou /WX.

Le code suivant génère maintenant l’erreur C4643 :

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

Pour corriger cette erreur, utilisez une directive #include plutôt qu’une déclaration anticipée :

#include <vector>

Constructeurs déléguant à eux-mêmes

La norme C++ suggère qu’un compilateur doit émettre un diagnostic quand un constructeur de délégation délègue à lui-même. Le compilateur Microsoft C++ en modes /std:c++17 et /std:c++latest lève désormais l’erreur C7535.

Sans cette erreur, la compilation du programme suivant aboutit, mais une boucle infinie est générée :

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

Pour éviter la boucle infinie, déléguez à un autre constructeur :

class X {
public:

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

offsetof avec expressions constantes

offsetof était traditionnellement implémenté à l’aide d’une macro nécessitant un reinterpret_cast. Cette utilisation n’est pas conforme dans les contextes nécessitant une expression constante, le compilateur Microsoft C++ l’a toujours autorisée. La macro offsetof fournie dans le cadre de la bibliothèque standard utilise correctement une fonction intrinsèque du compilateur (__builtin_offsetof), mais de nombreuses personnes utilisent l’astuce de la macro pour définir leur propre offsetof.

Dans Visual Studio 2017 version 15.8, le compilateur contraint les zones dans lesquelles ces opérateurs reinterpret_cast peuvent apparaître dans le mode par défaut pour améliorer la conformité du code au comportement C++ standard. Sous /permissive-, les contraintes sont encore plus strictes. L’utilisation du résultat d’un offsetof dans des emplacements nécessitant des expressions constantes peut produire un code qui émet un avertissement C4644 ou C2975.

Le code suivant lève l’erreur C4644 en modes par défaut et /std:c++17, et l’erreur C2975 en mode /permissive- :

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

Pour corriger cette erreur, utilisez offsetof tel que défini via <cstddef> :

#include <cstddef>

struct Data {
    int x;
};

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

qualificateurs cv sur les classes de base soumises à une expansion de pack

Les versions précédentes du compilateur Microsoft C++ ne détectaient pas la présence de qualificateurs cv dans une classe de base si celle-ci était également soumise à une expansion de pack.

Dans Visual Studio 2017 version 15.8, en mode /permissive-, le code suivant lève l’erreur 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
}

Mot clé template et spécificateurs de noms imbriqués

En mode /permissive-, le compilateur exige désormais que le mot clé template précède un nom de modèle quand celui-ci se situe après un spécificateur de nom imbriqué dépendant.

Le code suivant en mode /permissive- lève désormais l’erreur 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
    }
};

Pour corriger cette erreur, ajoutez le mot clé template à l’instruction Base<T>::example<int>();, comme illustré dans l’exemple suivant :

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

Améliorations de la conformité dans la version 15.9

Ordre d’évaluation de gauche à droite pour les opérateurs ->*, [], >>, et <<

À compter de C++17, les opérandes des opérateurs ->*, [], >>, et << doivent être évalués de gauche à droite. Il existe deux cas dans lesquels le compilateur est incapable de garantir cet ordre :

  • lorsqu’une des expressions de l’opérande est un objet passé par une valeur ou contient un objet passé par une valeur, ou

  • lors de la compilation à l’aide de /clr, et l’une des opérandes est un champ d’un objet ou un élément de tableau.

Le compilateur émet l’avertissement C4866 quand il ne peut pas garantir l’évaluation de gauche à droite. Cet avertissement est généré par le compilateur uniquement si /std:c++17 ou version ultérieure est spécifié, car l’exigence d’ordre de gauche à droite de ces opérateurs a été introduite dans C++17.

Pour résoudre cet avertissement, commencez par déterminer si l’évaluation de gauche à droite des opérandes est nécessaire. Par exemple, elle peut être nécessaire lorsque l’évaluation des opérandes risque de produire des effets secondaires dépendant de l’ordre. Dans de nombreux cas, l’ordre dans lequel les opérandes sont évalués n’a pas d’effet visible. Si l’ordre d’évaluation doit être de gauche à droite, réfléchissez si vous pouvez passer les opérandes par référence const à la place. Cette modification supprime l’avertissement dans l’exemple de code suivant :

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

Identificateurs dans les modèles d’alias de membre

Un identificateur utilisé dans une définition de modèle d’alias de membre doit être déclaré avant toute utilisation.

Dans les versions précédentes du compilateur, le code suivant était autorisé. Dans Visual Studio 2017 version 15.9, en mode /permissive-, le compilateur lève l’erreur 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;

Pour corriger cette erreur, déclarez from_template avant from_template_t.

Changements apportés aux modules

Dans Visual Studio 2017 version 15.9, le compilateur génère C5050 chaque fois que les options de ligne de commande pour les modules ne sont pas cohérentes entre la partie création et la partie consommation du module. L’exemple suivant présente deux problèmes :

  • Dans la partie consommation (main.cpp), l’option /EHsc n’est pas spécifiée.

  • La version de C++ est /std:c++17 côté création et /std:c++14 côté consommation.

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

Le compilateur lève l’avertissement C5050 pour ces cas :

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

Le compilateur lève aussi l’erreur C7536 chaque fois que le fichier .ifc est altéré. L’en-tête de l’interface de module contient un hachage SHA2 du contenu situé en dessous. Lors de l’importation, le fichier .ifc est haché, puis comparé au hachage fourni dans l’en-tête. Si les hachages ne correspondent pas, l’erreur C7536 est levée :

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

Classement partiel impliquant des alias et des contextes non déduits

Les implémentations des règles de classement partiel impliquant des alias dans des contextes non déduits font l’objet de divergences. Dans l’exemple suivant, GCC et le compilateur Microsoft C++ (en mode /permissive-) lèvent une erreur, tandis que Clang accepte le code.

#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’exemple précédent lève l’erreur 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 divergence de l’implémentation existe en raison d’une régression dans la formulation de la norme C++. La résolution du problème de base 2235 a supprimé le texte qui permettait le classement de ces surcharges. La norme C++ actuelle ne fournissant pas de mécanisme pour classer partiellement ces fonctions, elles sont considérées comme ambiguës.

Pour résoudre ce problème, nous vous recommandons de ne pas recourir au classement partiel. À la place, utilisez SFINAE pour supprimer des surcharges particulières. Dans l’exemple suivant, nous utilisons une classe d’assistance IsA pour supprimer la première surcharge quand Alloc est une spécialisation de 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;
}

Expressions non conformes et types non littéraux dans les définitions de fonctions basées sur un modèle

Les expressions non conformes et les types non littéraux sont maintenant correctement diagnostiqués dans les définitions de fonctions basées sur un modèle qui sont explicitement spécialisées. Auparavant, ces erreurs n’étaient pas émises pour la définition de fonction. Cependant, l’expression non conforme ou le type non littéral étaient néanmoins diagnostiqués s’ils étaient évalués dans le cadre d’une expression de constante.

Dans les versions précédentes de Visual Studio, le code suivant se compile sans avertissement :

void g();

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

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

Dans Visual Studio 2017 version 15.9, le code lève l’erreur 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'.

Pour éviter l’erreur, supprimez le qualificateur constexpr de l’instanciation explicite de la fonction f().

Voir aussi

Conformité du langage Microsoft C/C++