Namespaces
Variants

Class template argument deduction (CTAD) (since C++17)

From cppreference.net
C++ language
General topics
Flow control
Conditional execution statements
Iteration statements (loops)
Jump statements
Functions
Function declaration
Lambda function expression
inline specifier
Dynamic exception specifications ( until C++17* )
noexcept specifier (C++11)
Exceptions
Namespaces
Types
Specifiers
constexpr (C++11)
consteval (C++20)
constinit (C++20)
Storage duration specifiers
Initialization
Expressions
Alternative representations
Literals
Boolean - Integer - Floating-point
Character - String - nullptr (C++11)
User-defined (C++11)
Utilities
Attributes (C++11)
Types
typedef declaration
Type alias declaration (C++11)
Casts
Memory allocation
Classes
Class-specific function properties
Special member functions
Templates
Miscellaneous

Afin d'instancier un class template , chaque argument de template doit être connu, mais pas nécessairement spécifié. Dans les contextes suivants, le compilateur déduira les arguments de template à partir du type de l'initialiseur :

  • toute déclaration qui spécifie l'initialisation d'une variable et de modèle de variable, dont le type déclaré est le modèle de classe (éventuellement qualifié cv ) :
std::pair p(2, 4.5);     // déduit std::pair<int, double> p(2, 4.5);
std::tuple t(4, 3, 2.5); // identique à auto t = std::make_tuple(4, 3, 2.5);
std::less l;             // identique à std::less<void> l;
template<class T>
struct A
{
    A(T, T);
};
auto y = new A{1, 2}; // type alloué est A<int>
auto lck = std::lock_guard(mtx);     // déduit std::lock_guard<std::mutex>
std::copy_n(vi1, 3,
    std::back_insert_iterator(vi2)); // déduit std::back_insert_iterator<T>,
                                     // où T est le type du conteneur vi2
std::for_each(vi.begin(), vi.end(),
    Foo([&](int i) {...}));          // déduit Foo<T>,
                                     // où T est le type unique de la lambda
template<class T>
struct X
{
    constexpr X(T) {}
};
template<X x>
struct Y {};
Y<0> y; // OK, Y<X<int>(0)>
(depuis C++20)

Table des matières

Déduction pour les modèles de classe

Guides de déduction générés implicitement

Lorsque, dans un cast de style fonction ou dans la déclaration d'une variable, le spécificateur de type consiste uniquement en le nom d'un modèle de classe primaire C (c'est-à-dire qu'il n'y a pas de liste d'arguments de modèle associée), les candidats pour la déduction sont formés comme suit :

  • Si C est défini, pour chaque constructeur (ou modèle de constructeur) C i déclaré dans le modèle principal nommé, un modèle de fonction fictif F i est construit, de telle sorte que toutes les conditions suivantes soient satisfaites :
  • Les paramètres de template de F i sont les paramètres de template de C suivis (si C i est un template de constructeur) par les paramètres de template de C i (les arguments par défaut des templates sont également inclus).
  • Les contraintes associées de F i sont la conjonction des contraintes associées de C et des contraintes associées de C i .
(depuis C++20)
  • La liste de paramètres de F i est la liste de paramètres de C i .
  • Le type de retour de F i est C suivi des paramètres template de la classe template entre <> .
  • Si C n'est pas défini ou ne déclare aucun constructeur, une fonction template fictive supplémentaire est ajoutée, dérivée comme ci-dessus d'un constructeur hypothétique C() .
  • Dans tous les cas, un modèle de fonction fictif supplémentaire dérivé comme ci-dessus d'un constructeur hypothétique C(C) est ajouté, appelé le candidat de déduction de copie.
  • La liste de paramètres de F i est la liste de paramètres de G i .
  • Le type de retour de F i est l'identifiant de modèle simple de G i .
  • Si G i a des paramètres de modèle (syntaxe (2) ), F i est un modèle de fonction, et sa liste de paramètres de modèle est la liste de paramètres de modèle de G i . Sinon, F i est une fonction.
  • De plus, si
  • C est défini et satisfait les exigences d'un type agrégé en supposant que toute classe de base dépendante n'a pas de fonctions virtuelles ou de classes de base virtuelles,
  • il n'y a pas de guides de déduction définis par l'utilisateur pour C , et
  • la variable est initialisée à partir d'une liste non vide d'initialiseurs arg1, arg2, ..., argn (qui peut utiliser l'initialisateur désigné ),
un candidat de déduction agrégé peut être ajouté. La liste des paramètres du candidat de déduction agrégé est produite à partir des types d'éléments agrégés, comme suit :
  • Soit e i l' élément agrégé (éventuellement récursif) qui serait initialisé à partir de arg i , où
  • si l'expansion de paquet est un élément agrégé final, elle est considérée comme correspondant à tous les éléments restants de la liste d'initialisation ;
  • sinon, le paquet est considéré comme vide.
  • S'il n'existe pas de tel e i , le candidat de déduction agrégé n'est pas ajouté.
  • Sinon, déterminez la liste des paramètres T 1 , T 2 , ..., T n du candidat de déduction agrégé comme suit :
  • Si e i est un tableau et arg i est un braced-init-list , T i est une référence rvalue au type déclaré de e i .
  • Si e i est un tableau et arg i est un littéral de chaîne , T i est une référence lvalue au type déclaré const-qualifié de e i .
  • Sinon, T i est le type déclaré de e i .
  • Si un paquet a été ignoré car il s'agit d'un élément agrégé non final, un paquet de paramètres supplémentaire de la forme P j ... est inséré à sa position d'élément agrégé d'origine. (Cela entraînera généralement un échec de la déduction.)
  • Si un paquet est un élément agrégé final, la séquence finale de paramètres correspondante est remplacée par un seul paramètre de la forme T n ... .
Le candidat de déduction agrégé est un modèle de fonction fictif dérivé comme ci-dessus d'un constructeur hypothétique C(T 1 , T 2 , ..., T n ) .
Pendant la déduction d'argument de modèle pour le candidat de déduction agrégé, le nombre d'éléments dans un paquet de paramètres final n'est déduit qu'à partir du nombre d'arguments de fonction restants s'il n'est pas déduit autrement.
template<class T>
struct A
{
    T t;
    struct
    {
        long a, b;
    } u;
};
A a{1, 2, 3};
// aggregate deduction candidate:
//   template<class T>
//   A<T> F(T, long, long);
template<class... Args>
struct B : std::tuple<Args...>, Args... {};
B b{std::tuple<std::any, std::string>{}, std::any{}};
// aggregate deduction candidate:
//   template<class... Args>
//   B<Args...> F(std::tuple<Args...>, Args...);
// type of b is deduced as B<std::any, std::string>
(depuis C++20)

Déduction d'arguments de template et résolution de surcharge sont ensuite effectués pour l'initialisation d'un objet fictif de type classe hypothétique, dont les signatures de constructeur correspondent aux guides (sauf pour le type de retour) dans le but de former un ensemble de surcharge, et l'initialiseur est fourni par le contexte dans lequel la déduction d'arguments de template de classe a été effectuée, sauf que la première phase de initialisation de liste (considérant les constructeurs de liste d'initialisation) est omise si la liste d'initialisation consiste en une seule expression de type (éventuellement qualifié cv) U , où U est une spécialisation de C ou une classe dérivée d'une spécialisation de C .

Ces constructeurs fictifs sont des membres publics du type de classe hypothétique. Ils sont explicites si le guide a été formé à partir d'un constructeur explicite. Si la résolution de surcharge échoue, le programme est mal formé. Sinon, le type de retour de la spécialisation de modèle F sélectionnée devient la spécialisation de modèle de classe déduite.

template<class T>
struct UniquePtr
{
    UniquePtr(T* t);
};
UniquePtr dp{new auto(2.0)};
// Un constructeur déclaré :
// C1 : UniquePtr(T*);
// Ensemble des guides de déduction générés implicitement :
// F1 : template<class T>
//     UniquePtr<T> F(T* p);
// F2 : template<class T> 
//     UniquePtr<T> F(UniquePtr<T>); // candidat de déduction de copie
// classe imaginaire pour l'initialisation :
// struct X
// {
//     template<class T>
//     X(T* p);         // de F1
//     
//     template<class T>
//     X(UniquePtr<T>); // de F2
// };
// initialisation directe d'un objet X
// avec "new double(2.0)" comme initialiseur
// sélectionne le constructeur correspondant au guide F1 avec T = double
// Pour F1 avec T=double, le type de retour est UniquePtr<double>
// résultat :
// UniquePtr<double> dp{new auto(2.0)}

Ou, pour un exemple plus complexe (note : " S::N " ne compilerait pas : les qualificateurs de résolution de portée ne sont pas quelque chose qui peut être déduit) :

template<class T>
struct S
{
    template<class U>
    struct N
    {
        N(T);
        N(T, U);
        template<class V>
        N(V, U);
    };
};
S<int>::N x{2.0, 1};
// les guides de déduction générés implicitement sont (notez que T est déjà connu comme étant int)
// F1: template<class U>
//     S<int>::N<U> F(int);
// F2: template<class U>
//     S<int>::N<U> F(int, U);
// F3: template<class U, class V>
//     S<int>::N<U> F(V, U);
// F4: template<class U>
//     S<int>::N<U> F(S<int>::N<U>); (candidat de déduction de copie)
// La résolution de surcharge pour l'initialisation directe par liste avec "{2.0, 1}" comme initialiseur
// choisit F3 avec U=int et V=double.
// Le type de retour est S<int>::N<int>
// résultat :
// S<int>::N<int> x{2.0, 1};

Guides de déduction définis par l'utilisateur

La syntaxe d'un guide de déduction défini par l'utilisateur est la syntaxe d'une déclaration de fonction (template) avec un type de retour final, sauf qu'elle utilise le nom d'un template de classe comme nom de fonction :

explicit  (optionnel) template-name ( parameter-list ) -> simple-template-id requires-clause  (optionnel) ; (1)
template < template-parameter-list  > requires-clause  (optionnel)
explicit  (optionnel) template-name ( parameter-list ) -> simple-template-id requires-clause  (optionnel) ;
(2)
template-parameter-list - une liste non vide de paramètres de template séparés par des virgules
explicit - un explicit spécificateur
template-name - le nom du template de classe dont les arguments doivent être déduits
parameter-list - une liste de paramètres (éventuellement vide)
simple-template-id - un identifiant de template simple
requires-clause - (depuis C++20) une requires clause


Les paramètres des guides de déduction définis par l'utilisateur ne peuvent pas avoir de types d'espace réservé : la syntaxe de modèle de fonction abrégée n'est pas autorisée.

(since C++20)

Les guides de déduction définis par l'utilisateur doivent nommer un modèle de classe et doivent être introduits dans la même portée sémantique du modèle de classe (qui peut être un espace de noms ou une classe englobante) et, pour un modèle de classe membre, doivent avoir le même accès, mais les guides de déduction ne deviennent pas membres de cette portée.

Un guide de déduction n'est pas une fonction et ne possède pas de corps. Les guides de déduction ne sont pas trouvés par recherche de nom et ne participent pas à la résolution de surcharge, sauf pour la résolution de surcharge face à d'autres guides de déduction lors de la déduction des arguments de template de classe. Les guides de déduction ne peuvent pas être redéclarés dans la même unité de traduction pour le même template de classe.

// déclaration du template
template<class T>
struct container
{
    container(T t) {}
    template<class Iter>
    container(Iter beg, Iter end);
};
// guide de déduction supplémentaire
template<class Iter>
container(Iter b, Iter e) -> container<typename std::iterator_traits<Iter>::value_type>;
// utilisations
container c(7); // OK : déduit T=int en utilisant un guide généré implicitement
std::vector<double> v = {/* ... */};
auto d = container(v.begin(), v.end()); // OK : déduit T=double
container e{5, 6}; // Erreur : il n'y a pas de std::iterator_traits<int>::value_type

Les constructeurs fictifs à des fins de résolution de surcharge (décrits ci-dessus) sont explicites s'ils correspondent à un guide de déduction généré implicitement à partir d'un constructeur explicite ou à un guide de déduction défini par l'utilisateur qui est déclaré explicit . Comme toujours, ces constructeurs sont ignorés dans le contexte d'initialisation par copie :

template<class T>
struct A
{
    explicit A(const T&, ...) noexcept; // #1
    A(T&&, ...);                        // #2
};
int i;
A a1 = {i, i}; // erreur : ne peut pas déduire à partir de la référence rvalue dans #2,
               // et #1 est explicite, et n'est pas considéré dans l'initialisation par copie.
A a2{i, i};    // OK, #1 déduit A<int> et initialise également
A a3{0, i};    // OK, #2 déduit A<int> et initialise également
A a4 = {0, i}; // OK, #2 déduit A<int> et initialise également
template<class T>
A(const T&, const T&) -> A<T&>; // #3
template<class T>
explicit A(T&&, T&&)  -> A<T>;  // #4
A a5 = {0, 1}; // erreur : #3 déduit A<int&>
               // et #1 & #2 résultent en des constructeurs de paramètres identiques.
A a6{0, 1};    // OK, #4 déduit A<int> et #2 initialise
A a7 = {0, i}; // erreur : #3 déduit A<int&>
A a8{0, i};    // erreur : #3 déduit A<int&>
// Note : vérifier https://github.com/cplusplus/CWG/issues/647, indiquant que
// les exemples a7 et a8 sont incorrects, à remplacer potentiellement par
//A a7 = {0, i}; // erreur : #2 et #3 correspondent tous deux, la résolution de surcharge échoue
//A a8{i,i};     // erreur : #3 déduit A<int&>,
//               //        #1 et #2 déclarent le même constructeur

L'utilisation d'un typedef membre ou d'un alias de modèle dans la liste de paramètres d'un constructeur ou d'un constructeur de modèle ne rend pas, en soi, le paramètre correspondant du guide généré implicitement un contexte non déduit.

template<class T>
struct B
{
    template<class U>
    using TA = T;
    template<class U>
    B(U, TA<U>); // #1
};
// Le guide de déduction implicite généré à partir de #1 est équivalent à
//     template<class T, class U>
//     B(U, T) -> B<T>;
// plutôt que
//     template<class T, class U>
//     B(U, typename B<T>::template TA<U>) -> B<T>;
// qui n'aurait pas été déductible
B b{(int*)0, (char*)0}; // OK, déduit B<char*>

Déduction pour les alias de templates

Lorsqu'un cast de style fonction ou une déclaration de variable utilise le nom d'un alias de template A sans liste d'arguments comme spécificateur de type, où A est défini comme un alias de B<ArgList> , la portée de B est non dépendante, et B est soit un template de classe soit un alias de template défini de manière similaire, la déduction se déroulera de la même manière que pour les templates de classe, sauf que les guides sont générés à partir des guides de B , comme suit :

  • Pour chaque guide f de B , déduire les arguments template du type de retour de f à partir de B<ArgList> en utilisant la déduction d'arguments template , sauf que la déduction n'échoue pas si certains arguments ne sont pas déduits. Si la déduction échoue pour une autre raison, poursuivre avec un ensemble vide d'arguments template déduits.
  • Substituer le résultat de la déduction ci-dessus dans f , si la substitution échoue, aucun guide n'est produit ; sinon, soit g le résultat de la substitution, un guide f' est formé, tel que
  • Les types de paramètres et le type de retour de f' sont les mêmes que g
  • Si f est un template, f' est un template de fonction dont la liste de paramètres template comprend tous les paramètres template de A (y compris leurs arguments template par défaut) qui apparaissent dans les déductions ci-dessus ou (récursivement) dans leurs arguments template par défaut, suivis des paramètres template de f qui n'ont pas été déduits (y compris leurs arguments template par défaut) ; sinon ( f n'est pas un template), f' est une fonction
  • Les contraintes associées de f' sont la conjonction des contraintes associées de g et d'une contrainte qui est satisfaite si et seulement si les arguments de A sont déductibles à partir du type de résultat
template<class T>
class unique_ptr
{
    /* ... */
};
template<class T>
class unique_ptr<T[]>
{
    /* ... */
};
template<class T>
unique_ptr(T*) -> unique_ptr<T>;   // #1
template<class T>
unique_ptr(T*) -> unique_ptr<T[]>; // #2
template<class T>
concept NonArray = !std::is_array_v<T>;
template<NonArray A>
using unique_ptr_nonarray = unique_ptr<A>;
template<class A>
using unique_ptr_array = unique_ptr<A[]>;
// guide généré pour unique_ptr_nonarray :
// à partir de #1 (déduction de unique_ptr<T> à partir de unique_ptr<A> donne T = A) :
// template<class A>
//     requires(argument_of_unique_ptr_nonarray_is_deducible_from<unique_ptr<A>>)
// auto F(A*) -> unique_ptr<A>;
// à partir de #2 (déduction de unique_ptr<T[]> à partir de unique_ptr<A> ne donne rien) :
// template<class T>
//     requires(argument_of_unique_ptr_nonarray_is_deducible_from<unique_ptr<T[]>>)
// auto F(T*) -> unique_ptr<T[]>;
// où argument_of_unique_ptr_nonarray_is_deducible_from peut être défini comme
// template<class>
// class AA;
// template<NonArray A>
// class AA<unique_ptr_nonarray<A>> {};
// template<class T>
// concept argument_of_unique_ptr_nonarray_is_deducible_from =
//     requires { sizeof(AA<T>); };
// guide généré pour unique_ptr_array :
// à partir de #1 (déduction de unique_ptr<T> à partir de unique_ptr<A[]> donne T = A[]) :
// template<class A>
//     requires(argument_of_unique_ptr_array_is_deducible_from<unique_ptr<A[]>>)
// auto F(A(*)[]) -> unique_ptr<A[]>;
// à partir de #2 (déduction de unique_ptr<T[]> à partir de unique_ptr<A[]> donne T = A) :
// template<class A>
//     requires(argument_of_unique_ptr_array_is_deducible_from<unique_ptr<A[]>>)
// auto F(A*) -> unique_ptr<A[]>;
// où argument_of_unique_ptr_array_is_deducible_from peut être défini comme
// template<class>
// class BB;
// template<class A>
// class BB<unique_ptr_array<A>> {};
// template<class T>
// concept argument_of_unique_ptr_array_is_deducible_from =
//     requires { sizeof(BB<T>); };
// Utilisation :
unique_ptr_nonarray p(new int); // déduit en unique_ptr<int>
// le guide de déduction généré à partir de #1 retourne unique_ptr<int>
// le guide de déduction généré à partir de #2 retourne unique_ptr<int[]>, qui est ignoré car
//   argument_of_unique_ptr_nonarray_is_deducible_from<unique_ptr<int[]>> n'est pas satisfaite
unique_ptr_array q(new int[42]); // déduit en unique_ptr<int[]>
// le guide de déduction généré à partir de #1 échoue (ne peut pas déduire A dans A(*)[] à partir de new int[42])
// le guide de déduction généré à partir de #2 retourne unique_ptr<int[]>
(depuis C++20)

Notes

La déduction d'arguments de modèle de classe est uniquement effectuée si aucune liste d'arguments de modèle n'est présente. Si une liste d'arguments de modèle est spécifiée, la déduction n'a pas lieu.

std::tuple t1(1, 2, 3);                // OK : déduction
std::tuple<int, int, int> t2(1, 2, 3); // OK : tous les arguments sont fournis
std::tuple<> t3(1, 2, 3);    // Erreur : aucun constructeur correspondant dans tuple<>.
                             //        Aucune déduction effectuée.
std::tuple<int> t4(1, 2, 3); // Erreur

La déduction d'arguments de modèles de classe pour les agrégats nécessite généralement des guides de déduction définis par l'utilisateur :

template<class A, class B>
struct Agg
{
    A a;
    B b;
};
// implicitly-generated guides are formed from default, copy, and move constructors
template<class A, class B>
Agg(A a, B b) -> Agg<A, B>;
// ^ This deduction guide can be implicitly generated in C++20
Agg agg{1, 2.0}; // deduced to Agg<int, double> from the user-defined guide
template<class... T>
array(T&&... t) -> array<std::common_type_t<T...>, sizeof...(T)>;
auto a = array{1, 2, 5u}; // deduced to array<unsigned, 3> from the user-defined guide
(jusqu'en C++20)

Les guides de déduction définis par l'utilisateur ne doivent pas nécessairement être des modèles :

template<class T>
struct S
{
    S(T);
};
S(char const*) -> S<std::string>;
S s{"hello"}; // déduit en S<std::string>

Dans le cadre d'un modèle de classe, le nom du modèle sans liste de paramètres est un nom de classe injecté, et peut être utilisé comme un type. Dans ce cas, la déduction d'arguments de classe ne se produit pas et les paramètres du modèle doivent être fournis explicitement :

template<class T>
struct X
{
    X(T) {}
    template<class Iter>
    X(Iter b, Iter e) {}
    template<class Iter>
    auto foo(Iter b, Iter e)
    {
        return X(b, e); // pas de déduction : X est le X<T> actuel
    }
    template<class Iter>
    auto bar(Iter b, Iter e)
    {
        return X<typename Iter::value_type>(b, e); // doit spécifier ce que nous voulons
    }
    auto baz()
    {
        return ::X(0); // pas le nom injecté de la classe ; déduit comme X<int>
    }
};

Dans la résolution de surcharge , l'ordre partiel prend la priorité sur le fait qu'un modèle de fonction soit généré à partir d'un guide de déduction défini par l'utilisateur : si le modèle de fonction généré à partir du constructeur est plus spécialisé que celui généré à partir du guide de déduction défini par l'utilisateur, celui généré à partir du constructeur est choisi. Parce que le candidat de déduction de copie est généralement plus spécialisé qu'un constructeur d'encapsulation, cette règle signifie que la copie est généralement préférée à l'encapsulation.

template<class T>
struct A
{
    A(T, int*);     // #1
    A(A<T>&, int*); // #2
    enum { value };
};
template<class T, int N = T::value>
A(T&&, int*) -> A<T>; //#3
A a{1, 0}; // utilise #1 pour déduire A<int> et initialise avec #1
A b{a, 0}; // utilise #2 (plus spécialisé que #3) pour déduire A<int> et initialise avec #2

Lorsque les critères de départage antérieurs, y compris l'ordonnancement partiel, ne parviennent pas à distinguer deux modèles de fonction candidats, les règles suivantes s'appliquent :

  • Un modèle de fonction généré à partir d'un guide de déduction défini par l'utilisateur est préféré à celui généré implicitement à partir d'un constructeur ou d'un modèle de constructeur.
  • Le candidat de déduction de copie est préféré à tous les autres modèles de fonction générés implicitement à partir d'un constructeur ou d'un modèle de constructeur.
  • Un modèle de fonction généré implicitement à partir d'un constructeur non-modèle est préféré à un modèle de fonction généré implicitement à partir d'un modèle de constructeur.
template<class T>
struct A
{
    using value_type = T;
    A(value_type); // #1
    A(const A&);   // #2
    A(T, T, int);  // #3
    template<class U>
    A(int, T, U);  // #4
};                 // #5, le candidat de déduction de copie A(A);
A x(1, 2, 3); // utilise #3, généré à partir d'un constructeur non-template
template<class T>
A(T) -> A<T>; // #6, moins spécialisé que #5
A a(42); // utilise #6 pour déduire A<int> et #1 pour initialiser
A b = a; // utilise #5 pour déduire A<int> et #2 pour initialiser
template<class T>
A(A<T>) -> A<A<T>>; // #7, aussi spécialisé que #5
A b2 = a; // utilise #7 pour déduire A<A<int>> et #1 pour initialiser

Une référence à une valeur de déplacement vers un paramètre de modèle non qualifié cv n'est pas une référence de transfert si ce paramètre est un paramètre de modèle de classe :

template<class T>
struct A
{
    template<class U>
    A(T&&, U&&, int*); // #1 : T&& n'est pas une référence de transfert
                       //     U&& est une référence de transfert
    A(T&&, int*);      // #2 : T&& n'est pas une référence de transfert
};
template<class T>
A(T&&, int*) -> A<T>; // #3 : T&& est une référence de transfert
int i, *ip;
A a{i, 0, ip};  // erreur, ne peut pas déduire à partir de #1
A a0{0, 0, ip}; // utilise #1 pour déduire A<int> et #1 pour initialiser
A a2{i, ip};    // utilise #3 pour déduire A<int&> et #2 pour initialiser

Lors de l'initialisation à partir d'un seul argument d'un type qui est une spécialisation du modèle de classe en question, la déduction de copie est généralement préférée à l'encapsulation par défaut :

std::tuple t1{1};  //std::tuple<int>
std::tuple t2{t1}; //std::tuple<int>, pas std::tuple<std::tuple<int>>
std::vector v1{1, 2};   // std::vector<int>
std::vector v2{v1};     // std::vector<int>, pas std::vector<std::vector<int>> (P0702R1)
std::vector v3{v1, v2}; // std::vector<std::vector<int>>

En dehors du cas particulier de la copie par rapport à l'encapsulation, la forte préférence pour les constructeurs de liste d'initialisation dans l'initialisation de liste reste intacte.

std::vector v1{1, 2}; // std::vector<int>
std::vector v2(v1.begin(), v1.end()); // std::vector<int>
std::vector v3{v1.begin(), v1.end()}; // std::vector<std::vector<int>::iterator>
**Note:** Le code C++ n'a pas été traduit conformément aux instructions, car il se trouve dans des balises `
` et contient des termes spécifiques au C++. Seul le texte des commentaires a été traduit en français :
- `// std::vector` → `// std::vector` (inchangé - terme technique)
- `// std::vector` → `// std::vector` (inchangé - terme technique)  
- `// std::vector::iterator>` → `// std::vector::iterator>` (inchangé - terme technique)
Aucune autre traduction n'était nécessaire puisque le reste du contenu est soit du code C++, soit des balises HTML.

Avant l'introduction de la déduction d'arguments de modèle de classe, une approche courante pour éviter de spécifier explicitement les arguments consistait à utiliser un modèle de fonction :

std::tuple p1{1, 1.0};             //std::tuple<int, double>, utilisant la déduction
auto p2 = std::make_tuple(1, 1.0); //std::tuple<int, double>, pré-C++17
Macro de test de fonctionnalité Valeur Std Fonctionnalité
__cpp_deduction_guides 201703L (C++17) Déduction d'arguments de template pour les templates de classe
201907L (C++20) CTAD pour les agrégats et alias

Rapports de défauts

Les rapports de défauts modifiant le comportement suivants ont été appliqués rétroactivement aux normes C++ précédemment publiées.

DR Appliqué à Comportement publié Comportement corrigé
CWG 2376 C++17 CTAD était effectué même si le type de la variable déclarée est
différent du modèle de classe dont les arguments seront déduits
ne pas effectuer
CTAD dans ce cas
CWG 2628 C++20 les guides de déduction implicites ne propageaient pas les contraintes propager les contraintes
CWG 2697 C++20 il n'était pas clair si la syntaxe abrégée des modèles de fonction
est autorisée dans les guides de déduction définis par l'utilisateur
interdite
CWG 2707 C++20 les guides de déduction ne pouvaient pas avoir une clause requires finale ils le peuvent
CWG 2714 C++17 les guides de déduction implicites ne prenaient pas en compte
les arguments par défaut des constructeurs
les prendre en compte
CWG 2913 C++20 la résolution de CWG issue 2707 a rendu la syntaxe des guides de déduction
incompatible avec la syntaxe de déclaration de fonction
syntaxe ajustée
P0702R1 C++17 un constructeur de liste d'initialisation peut supplanter le
candidat de déduction de copie, entraînant un encapsulage
phase de liste d'initialisation
ignorée lors de la copie