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

Um eine Klassenvorlage zu instanziieren, müssen alle Template-Argumente bekannt sein, aber nicht jedes Template-Argument muss angegeben werden. In den folgenden Kontexten leitet der Compiler die Template-Argumente aus dem Typ des Initialisierers ab:

  • jede Deklaration die eine Initialisierung einer Variable und Variablen-Template spezifiziert, deren deklarierter Typ das Klassentemplate ist (möglicherweise cv-qualifiziert ):
std::pair p(2, 4.5);     // ergibt std::pair<int, double> p(2, 4.5);
std::tuple t(4, 3, 2.5); // gleich wie auto t = std::make_tuple(4, 3, 2.5);
std::less l;             // gleich wie std::less<void> l;
template<class T>
struct A
{
    A(T, T);
};
auto y = new A{1, 2}; // zugewiesener Typ ist A<int>
Die Übersetzung des Kommentars "// allocated type is A<int>" wurde zu "// zugewiesener Typ ist A<int>" geändert, während alle HTML-Tags, Attribute und C++-Code unverändert blieben.
auto lck = std::lock_guard(mtx);     // leitet sich zu std::lock_guard<std::mutex> ab
std::copy_n(vi1, 3,
    std::back_insert_iterator(vi2)); // leitet sich zu std::back_insert_iterator<T> ab,
                                     // wobei T der Typ des Containers vi2 ist
std::for_each(vi.begin(), vi.end(),
    Foo([&](int i) {...}));          // leitet sich zu Foo<T> ab,
                                     // wobei T der eindeutige Lambda-Typ ist
template<class T>
struct X
{
    constexpr X(T) {}
};
template<X x>
struct Y {};
Y<0> y; // OK, Y<X<int>(0)>
(seit C++20)

Inhaltsverzeichnis

Deduktion für Klassentemplates

Implizit generierte Deduction Guides

Wenn in einem Funktionsstil-Cast oder in der Deklaration einer Variable der Typspezifizierer ausschließlich aus dem Namen einer primären Klassenvorlage C besteht (d.h., es gibt keine begleitende Template-Argumentliste), werden Kandidaten für die Ableitung wie folgt gebildet:

  • Falls C definiert ist, wird für jeden Konstruktor (oder Konstruktor-Template) C i , der in der benannten primären Template-Deklaration deklariert ist, eine fiktive Funktions-Template F i erstellt, sodass alle folgenden Bedingungen erfüllt sind:
  • Die Template-Parameter von F i sind die Template-Parameter von C gefolgt (falls C i ein Konstruktor-Template ist) von den Template-Parametern von C i (Standard-Template-Argumente sind ebenfalls enthalten).
  • Die assoziierten Constraints von F i sind die Konjunktion der assoziierten Constraints von C und der assoziierten Constraints von C i .
(seit C++20)
  • Die Parameterliste von F i ist die Parameterliste von C i .
  • Der Rückgabetyp von F i ist C gefolgt von den Template-Parametern der Klassenvorlage, eingeschlossen in <> .
  • Wenn C nicht definiert ist oder keine Konstruktoren deklariert, wird eine zusätzliche fiktive Funktionsvorlage hinzugefügt, die wie oben von einem hypothetischen Konstruktor C() abgeleitet wird.
  • In jedem Fall wird eine zusätzliche fiktive Funktionsvorlage, die wie oben von einem hypothetischen Konstruktor C(C) abgeleitet wurde, hinzugefügt, genannt der Copy Deduction Candidate.
  • Die Parameterliste von F i ist die Parameterliste von G i .
  • Der Rückgabetyp von F i ist die einfache Template-Kennung von G i .
  • Wenn G i Template-Parameter besitzt (Syntax (2) ), F i ist ein Funktions-Template, und seine Template-Parameterliste ist die Template-Parameterliste von G i . Andernfalls ist F i eine Funktion.
  • Zusätzlich, falls
  • C definiert ist und die Anforderungen eines Aggregattyps erfüllt unter der Annahme, dass jede abhängige Basisklasse keine virtuellen Funktionen oder virtuellen Basisklassen hat,
  • keine benutzerdefinierten Deduktionsführer für C existieren, und
  • die Variable aus einer nicht-leeren Liste von Initialisierern arg1, arg2, ..., argn initialisiert wird (die Designated Initializer verwenden können),
kann ein Aggregat-Deduktionskandidat hinzugefügt werden. Die Parameterliste des Aggregat-Deduktionskandidaten wird aus den Aggregat-Elementtypen wie folgt erzeugt:
  • Sei e i das (möglicherweise rekursive) Aggregatelement , das von arg i initialisiert werden würde, wobei
  • falls C (oder sein Element, das selbst ein Aggregat ist) eine Basis hat, die eine Pack-Erweiterung ist:
  • wenn die Pack-Erweiterung ein nachfolgendes Aggregatelement ist, wird angenommen, dass sie alle verbleibenden Elemente der Initialisiererliste abdeckt;
  • andernfalls wird das Pack als leer betrachtet.
  • Falls kein solches e i existiert, wird der Aggregat-Deduktionskandidat nicht hinzugefügt.
  • Andernfalls wird die Parameterliste T 1 , T 2 , ..., T n des Aggregat-Deduktionskandidaten wie folgt bestimmt:
  • Falls e i ein Array ist und arg i ein braced-init-list  ist, T i ist ein Rvalue-Referenz auf den deklarierten Typ von e i .
  • Falls e i ein Array ist und arg i ein String-Literal ist, T i ist eine Lvalue-Referenz auf den const-qualifizierten deklarierten Typ von e i .
  • Andernfalls ist T i der deklarierte Typ von e i .
  • Falls ein Pack übersprungen wurde, weil es ein nicht-nachfolgendes Aggregatelement ist, wird ein zusätzlicher Parameter-Pack der Form P j ... an seiner ursprünglichen Aggregatelementposition eingefügt. (Dies führt im Allgemeinen zu einem Deduktionsfehler.)
  • Falls ein Pack ein nachfolgendes Aggregatelement ist, wird die nachfolgende Parameterfolge, die ihm entspricht, durch einen einzelnen Parameter der Form T n ... ersetzt.
Der Aggregat-Deduktionskandidat ist eine fiktive Funktionsvorlage, die wie oben von einem hypothetischen Konstruktor C(T 1 , T 2 , ..., T n ) abgeleitet wird.
Während der Template-Argumentdeduktion für den Aggregat-Deduktionskandidaten wird die Anzahl der Elemente in einem nachfolgenden Parameter-Pack nur aus der Anzahl der verbleibenden Funktionsargumente deduziert, falls sie nicht anderweitig deduziert wird.
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>
(seit C++20)

Template Argument Deduction und Overload Resolution werden dann zur Initialisierung eines fiktiven Objekts eines hypothetischen Klassentyps durchgeführt, dessen Konstruktorsignaturen mit den Guides übereinstimmen (außer im Rückgabetyp), um einen Overload-Satz zu bilden. Der Initialisierer wird durch den Kontext bereitgestellt, in dem die Klassentemplate-Argumentableitung durchgeführt wurde, mit der Ausnahme, dass die erste Phase der Listeninitialisierung (Betrachtung von Initializer-List-Konstruktoren) weggelassen wird, wenn die Initialisierungsliste aus einem einzelnen Ausdruck vom Typ (möglicherweise cv-qualifiziert) U besteht, wobei U eine Spezialisierung von C oder eine von einer Spezialisierung von C abgeleitete Klasse ist.

Diese fiktiven Konstruktoren sind öffentliche Mitglieder des hypothetischen Klassentyps. Sie sind explizit, wenn der Leitfaden aus einem expliziten Konstruktor gebildet wurde. Wenn die Überladungsauflösung fehlschlägt, ist das Programm fehlerhaft. Andernfalls wird der Rückgabetyp der ausgewählten F Template-Spezialisierung zur deduzierten Klassentemplate-Spezialisierung.

template<class T>
struct UniquePtr
{
    UniquePtr(T* t);
};
UniquePtr dp{new auto(2.0)};
// Ein deklarierter Konstruktor:
// C1: UniquePtr(T*);
// Menge implizit generierter Ableitungsleitfäden:
// F1: template<class T>
//     UniquePtr<T> F(T* p);
// F2: template<class T> 
//     UniquePtr<T> F(UniquePtr<T>); // Kopier-Ableitungskandidat
// Imaginäre Klasse zur Initialisierung:
// struct X
// {
//     template<class T>
//     X(T* p);         // von F1
//     
//     template<class T>
//     X(UniquePtr<T>); // von F2
// };
// Direktinitialisierung eines X-Objekts
// mit "new double(2.0)" als Initialisierer
// wählt den Konstruktor, der dem Leitfaden F1 mit T = double entspricht
// Für F1 mit T=double ist der Rückgabetyp UniquePtr<double>
// Ergebnis:
// UniquePtr<double> dp{new auto(2.0)}

Oder, für ein komplexeres Beispiel (beachten Sie: " S::N " würde nicht kompilieren: Bereichsauflösungsqualifizierer sind nichts, was abgeleitet werden kann):

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};
// die implizit generierten Ableitungsleitlinien sind (beachte, dass T bereits als int bekannt ist)
// 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>); (Kopier-Ableitungskandidat)
// Überladungsauflösung für Direct-List-Initialisierung mit "{2.0, 1}" als Initialisierer
// wählt F3 mit U=int und V=double.
// Der Rückgabetyp ist S<int>::N<int>
// Ergebnis:
// S<int>::N<int> x{2.0, 1};

Benutzerdefinierte Deduktionsanleitungen

Die Syntax eines benutzerdefinierten Deduction Guide ist die Syntax einer Funktions-(Template-)Deklaration mit nachgestelltem Rückgabetyp, mit der Ausnahme, dass sie den Namen eines Klassentemplates als Funktionsnamen verwendet:

explicit  (optional) template-name ( parameter-list ) -> simple-template-id requires-clause  (optional) ; (1)
template < template-parameter-list  > requires-clause  (optional)
explicit  (optional) template-name ( parameter-list ) -> simple-template-id requires-clause  (optional) ;
(2)
template-parameter-list - eine nicht-leere, kommagetrennte Liste von Template-Parametern
explicit - ein explicit Specifier
template-name - der Name des Klassentemplates, dessen Argumente abgeleitet werden sollen
parameter-list - eine (möglicherweise leere) Parameterliste
simple-template-id - eine einfache Template-Kennung
requires-clause - (seit C++20) eine requires Klausel


Die Parameter von benutzerdefinierten Deduktionsanleitern können keine Platzhaltertypen haben: Die Abbreviated Function Template -Syntax ist nicht zulässig.

(since C++20)

Benutzerdefinierte Deduktionsanleitungen müssen einen Klassentemplate benennen und müssen innerhalb desselben semantischen Geltungsbereichs des Klassentemplates (dies könnte ein Namensraum oder eine umschließende Klasse sein) eingeführt werden und müssen für einen Member-Klassentemplate denselben Zugriff haben, aber Deduktionsanleitungen werden nicht zu Membern dieses Geltungsbereichs.

Ein Ableitungsleitfaden ist keine Funktion und hat keinen Körper. Ableitungsleitfäden werden nicht durch Namenssuche gefunden und nehmen nicht an der Überladungsauflösung teil, außer bei der Überladungsauflösung gegenüber anderen Ableitungsleitfäden bei der Ableitung von Klassentemplate-Argumenten. Ableitungsleitfäden können nicht in derselben Übersetzungseinheit für dasselbe Klassentemplate erneut deklariert werden.

// Deklaration des Templates
template<class T>
struct container
{
    container(T t) {}
    template<class Iter>
    container(Iter beg, Iter end);
};
// Zusätzlicher Deduction Guide
template<class Iter>
container(Iter b, Iter e) -> container<typename std::iterator_traits<Iter>::value_type>;
// Verwendungen
container c(7); // OK: leitet T=int mit einem implizit generierten Guide ab
std::vector<double> v = {/* ... */};
auto d = container(v.begin(), v.end()); // OK: leitet T=double ab
container e{5, 6}; // Fehler: es gibt kein std::iterator_traits<int>::value_type

Die fiktiven Konstruktoren zum Zweck der Überladungsauflösung (oben beschrieben) sind explizit, wenn sie einem implizit generierten Ableitungsleitfaden entsprechen, der von einem expliziten Konstruktor gebildet wurde, oder einem benutzerdefinierten Ableitungsleitfaden, der als explicit deklariert ist. Wie immer werden solche Konstruktoren im Copy-Initialisierungskontext ignoriert:

template<class T>
struct A
{
    explicit A(const T&, ...) noexcept; // #1
    A(T&&, ...);                        // #2
};
int i;
A a1 = {i, i}; // Fehler: Kann nicht von Rvalue-Referenz in #2 ableiten,
               // und #1 ist explicit und wird bei Copy-Initialisierung nicht berücksichtigt.
A a2{i, i};    // OK, #1 leitet auf A<int> ab und initialisiert ebenfalls
A a3{0, i};    // OK, #2 leitet auf A<int> ab und initialisiert ebenfalls
A a4 = {0, i}; // OK, #2 leitet auf A<int> ab und initialisiert ebenfalls
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}; // Fehler: #3 leitet auf A<int&> ab
               // und #1 & #2 führen zu denselben Parameterkonstruktoren.
A a6{0, 1};    // OK, #4 leitet auf A<int> ab und #2 initialisiert
A a7 = {0, i}; // Fehler: #3 leitet auf A<int&> ab
A a8{0, i};    // Fehler: #3 leitet auf A<int&> ab
// Hinweis: Prüfe https://github.com/cplusplus/CWG/issues/647, das behauptet,
// dass die Beispiele a7 und a8 inkorrekt sind und möglicherweise ersetzt werden als
//A a7 = {0, i}; // Fehler: #2 und #3 passen beide, Überladungsauflösung schlägt fehl
//A a8{i,i};     // Fehler: #3 leitet auf A<int&> ab,
//               //        #1 und #2 deklarieren denselben Konstruktor

Die Verwendung eines Member-Typedefs oder Alias-Templates in der Parameterliste eines Konstruktors oder Konstruktor-Templates macht den entsprechenden Parameter des implizit generierten Guides nicht per se zu einem nicht abgeleiteten Kontext.

template<class T>
struct B
{
    template<class U>
    using TA = T;
    template<class U>
    B(U, TA<U>); // #1
};
// Der implizit generierte Ableitungsleitfaden von #1 entspricht
//     template<class T, class U>
//     B(U, T) -> B<T>;
// und nicht
//     template<class T, class U>
//     B(U, typename B<T>::template TA<U>) -> B<T>;
// was nicht ableitbar gewesen wäre
B b{(int*)0, (char*)0}; // OK, leitet B<char*> ab

Deduktion für Alias-Templates

Wenn ein Funktionsstil-Cast oder die Deklaration einer Variable den Namen eines Alias-Templates A ohne Argumentliste als Typspezifizierer verwendet, wobei A als Alias von B<ArgList> definiert ist, der Gültigkeitsbereich von B nicht-abhängig ist und B entweder ein Klassentemplate oder ein ähnlich definiertes Alias-Template ist, erfolgt die Deduktion auf die gleiche Weise wie für Klassentemplates, außer dass die Guides stattdessen aus den Guides von B generiert werden, wie folgt:

  • Für jeden Guide f von B leite die Template-Argumente des Rückgabetyps von f aus B<ArgList> unter Verwendung von Template-Argument-Deduktion ab, außer dass die Deduktion nicht fehlschlägt, wenn einige Argumente nicht abgeleitet werden. Wenn die Deduktion aus einem anderen Grund fehlschlägt, fahre mit einer leeren Menge abgeleiteter Template-Argumente fort.
  • Substituiere das Ergebnis der obigen Deduktion in f ; wenn die Substitution fehlschlägt, wird kein Guide erzeugt; andernfalls bezeichne g das Ergebnis der Substitution, ein Guide f' wird gebildet, so dass
  • Die Parametertypen und der Rückgabetyp von f' sind die gleichen wie bei g
  • Wenn f ein Template ist, ist f' ein Funktions-Template, dessen Template-Parameterliste aus allen Template-Parametern von A (einschließlich ihrer Standard-Template-Argumente) besteht, die in den obigen Deduktionen oder (rekursiv) in ihren Standard-Template-Argumenten vorkommen, gefolgt von den Template-Parametern von f , die nicht abgeleitet wurden (einschließlich ihrer Standard-Template-Argumente); andernfalls ( f ist kein Template) ist f' eine Funktion
  • Die assoziierten Constraints von f' sind die Konjunktion der assoziierten Constraints von g und eines Constraints, das genau dann erfüllt ist, wenn die Argumente von A aus dem Ergebnistyp ableitbar sind
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[]>;
// generierter Guide für unique_ptr_nonarray:
// aus #1 (Deduktion von unique_ptr<T> aus unique_ptr<A> ergibt T = A):
// template<class A>
//     requires(argument_of_unique_ptr_nonarray_is_deducible_from<unique_ptr<A>>)
// auto F(A*) -> unique_ptr<A>;
// aus #2 (Deduktion von unique_ptr<T[]> aus unique_ptr<A> ergibt nichts):
// template<class T>
//     requires(argument_of_unique_ptr_nonarray_is_deducible_from<unique_ptr<T[]>>)
// auto F(T*) -> unique_ptr<T[]>;
// wobei argument_of_unique_ptr_nonarray_is_deducible_from definiert werden kann als
// 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>); };
// generierter Guide für unique_ptr_array:
// aus #1 (Deduktion von unique_ptr<T> aus unique_ptr<A[]> ergibt T = A[]):
// template<class A>
//     requires(argument_of_unique_ptr_array_is_deducible_from<unique_ptr<A[]>>)
// auto F(A(*)[]) -> unique_ptr<A[]>;
// aus #2 (Deduktion von unique_ptr<T[]> aus unique_ptr<A[]> ergibt T = A):
// template<class A>
//     requires(argument_of_unique_ptr_array_is_deducible_from<unique_ptr<A[]>>)
// auto F(A*) -> unique_ptr<A[]>;
// wobei argument_of_unique_ptr_array_is_deducible_from definiert werden kann als
// 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>); };
// Verwendung:
unique_ptr_nonarray p(new int); // deduziert zu unique_ptr<int>
// Deduktion-Guide generiert aus #1 gibt unique_ptr<int> zurück
// Deduktion-Guide generiert aus #2 gibt unique_ptr<int[]> zurück, was ignoriert wird, weil
//   argument_of_unique_ptr_nonarray_is_deducible_from<unique_ptr<int[]>> unerfüllt ist
unique_ptr_array q(new int[42]); // deduziert zu unique_ptr<int[]>
// Deduktion-Guide generiert aus #1 schlägt fehl (kann A in A(*)[] nicht aus new int[42] deduzieren)
// Deduktion-Guide generiert aus #2 gibt unique_ptr<int[]> zurück
(seit C++20)

Hinweise

Die Ableitung von Klassentemplate-Argumenten wird nur durchgeführt, wenn keine Template-Argumentliste vorhanden ist. Wenn eine Template-Argumentliste angegeben wird, findet keine Ableitung statt.

std::tuple t1(1, 2, 3);                // OK: Ableitung
std::tuple<int, int, int> t2(1, 2, 3); // OK: Alle Argumente sind angegeben
std::tuple<> t3(1, 2, 3);    // Fehler: Kein passender Konstruktor in tuple<>.
                             //        Keine Ableitung durchgeführt.
std::tuple<int> t4(1, 2, 3); // Fehler

Die Argumentdeduktion von Klassentemplates für Aggregate erfordert typischerweise benutzerdefinierte Deduktionsanleitungen:

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
(bis C++20)

Benutzerdefinierte Deduktionsleitlinien müssen keine Templates sein:

template<class T>
struct S
{
    S(T);
};
S(char const*) -> S<std::string>;
S s{"hello"}; // abgeleitet als S<std::string>

Im Geltungsbereich eines Klassentemplates ist der Name des Templates ohne Parameterliste ein injizierter Klassenname und kann als Typ verwendet werden. In diesem Fall findet keine Klassenargumentableitung statt und Template-Parameter müssen explizit angegeben werden:

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); // Keine Deduktion: X ist das aktuelle X<T>
    }
    template<class Iter>
    auto bar(Iter b, Iter e)
    {
        return X<typename Iter::value_type>(b, e); // Muss explizit angeben, was wir wollen
    }
    auto baz()
    {
        return ::X(0); // Nicht der injizierte Klassenname; deduziert zu X<int>
    }
};

Bei der Überlagerungsauflösung hat die partielle Ordnung Vorrang vor der Frage, ob eine Funktionsvorlage aus einer benutzerdefinierten Deduktionsanleitung generiert wird: Wenn die aus dem Konstruktor generierte Funktionsvorlage spezialisierter ist als die aus der benutzerdefinierten Deduktionsanleitung generierte, wird die aus dem Konstruktor generierte gewählt. Da der Kopierdeduktionskandidat typischerweise spezialisierter ist als ein umschließender Konstruktor, bedeutet diese Regel, dass das Kopieren generell dem Umschließen vorgezogen wird.

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}; // verwendet #1 zur Ableitung von A<int> und Initialisierung mit #1
A b{a, 0}; // verwendet #2 (spezialisierter als #3) zur Ableitung von A<int> und Initialisierung mit #2

Wenn frühere Tiebreaker, einschließlich partieller Ordnung, nicht zwischen zwei Kandidaten-Funktionsvorlagen unterscheiden konnten, gelten die folgenden Regeln:

  • Ein von einem benutzerdefinierten Ableitungsleitfaden generiertes Funktions-Template wird gegenüber einem implizit aus einem Konstruktor oder Konstruktor-Template generierten bevorzugt.
  • Der Kopier-Ableitungskandidat wird gegenüber allen anderen implizit aus einem Konstruktor oder Konstruktor-Template generierten Funktions-Templates bevorzugt.
  • Ein implizit aus einem Nicht-Template-Konstruktor generiertes Funktions-Template wird gegenüber einem implizit aus einem Konstruktor-Template generierten Funktions-Template bevorzugt.
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, das Kopierdeduktionskandidat A(A);
A x(1, 2, 3); // verwendet #3, generiert von einem Nicht-Template-Konstruktor
template<class T>
A(T) -> A<T>; // #6, weniger spezialisiert als #5
A a(42); // verwendet #6 zur Deduktion von A<int> und #1 zur Initialisierung
A b = a; // verwendet #5 zur Deduktion von A<int> und #2 zur Initialisierung
template<class T>
A(A<T>) -> A<A<T>>; // #7, ebenso spezialisiert wie #5
A b2 = a; // verwendet #7 zur Deduktion von A<A<int>> und #1 zur Initialisierung

Eine Rvalue-Referenz auf einen cv-unqualifizierten Template-Parameter ist kein Forwarding-Reference , wenn dieser Parameter ein Klassen-Template-Parameter ist:

template<class T>
struct A
{
    template<class U>
    A(T&&, U&&, int*); // #1: T&& ist kein Forwarding-Reference
                       //     U&& ist eine Forwarding-Reference
    A(T&&, int*);      // #2: T&& ist kein Forwarding-Reference
};
template<class T>
A(T&&, int*) -> A<T>; // #3: T&& ist eine Forwarding-Reference
int i, *ip;
A a{i, 0, ip};  // Fehler, kann von #1 nicht ableiten
A a0{0, 0, ip}; // verwendet #1 um A<int> abzuleiten und #1 zur Initialisierung
A a2{i, ip};    // verwendet #3 um A<int&> abzuleiten und #2 zur Initialisierung

Bei der Initialisierung aus einem einzelnen Argument eines Typs, der eine Spezialisierung der betreffenden Klassenvorlage ist, wird das Kopierdeduktionsverfahren generell gegenüber dem Standard-Wrapping bevorzugt:

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

Außerhalb des Sonderfalls für Kopieren vs. Umhüllen bleibt die starke Präferenz für Initialisierer-Listen-Konstruktoren in der Listeninitialisierung intakt.

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>

Vor der Einführung von Template-Argumentableitung für Klassen war ein gängiger Ansatz, um das explizite Angeben von Argumenten zu vermeiden, die Verwendung eines Funktions-Templates:

std::tuple p1{1, 1.0};             //std::tuple<int, double>, Verwendung von Deduktion
auto p2 = std::make_tuple(1, 1.0); //std::tuple<int, double>, vor C++17
Feature-Test-Makro Wert Std Feature
__cpp_deduction_guides 201703L (C++17) Template-Argument-Deduktion für Klassentemplates
201907L (C++20) CTAD für Aggregate und Aliase

Fehlerberichte

Die folgenden verhaltensändernden Fehlerberichte wurden rückwirkend auf zuvor veröffentlichte C++-Standards angewendet.

DR Angewendet auf Verhalten wie veröffentlicht Korrektes Verhalten
CWG 2376 C++17 CTAD würde auch dann durchgeführt, wenn der Typ der deklarierten Variable
sich von der Klassenvorlage unterscheidet, deren Argumente abgeleitet werden sollen
CTAD in diesem Fall
nicht durchführen
CWG 2628 C++20 implizite Ableitungsleitlinien propagierten Einschränkungen nicht Einschränkungen propagieren
CWG 2697 C++20 es war unklar, ob die abgekürzte Funktionsvorlagen-
Syntax in benutzerdefinierten Ableitungsleitlinien erlaubt ist
verboten
CWG 2707 C++20 Ableitungsleitlinien konnten keine nachgestellte requires -Klausel haben sie können
CWG 2714 C++17 implizite Ableitungsleitlinien berücksichtigten
nicht die Standardargumente von Konstruktoren
diese berücksichtigen
CWG 2913 C++20 die Lösung von CWG Issue 2707 machte die Syntax der Ableitungsleitlinie
inkonsistent mit der Funktionsdeklarationssyntax
Syntax angepasst
P0702R1 C++17 ein Initializer-List-Konstruktor kann den
Kopierableitungskandidaten verdrängen, was zu Wrapping führt
Initializer-List-Phase
beim Kopieren übersprungen