Namensräume
Varianten
Aktionen

Klassenschablonen-Argumentableitung (CTAD) (seit C++17)

Von cppreference.com
< cpp‎ | Sprache
 
 
C++ Sprache
Allgemeine Themen
Kontrollfluss
Bedingte Ausführungsaussagen
if
Iterationsanweisungen (Schleifen)
for
Bereichs-for (C++11)
Sprunganweisungen
Funktionen
Funktionsdeklaration
Lambda-Funktionsausdruck
inline-Spezifizierer
Dynamische Ausnahmespezifikationen (bis C++17*)
noexcept-Spezifizierer (C++11)
Ausnahmen
Namensräume
Typen
Spezifizierer
const/volatile
decltype (C++11)
auto (C++11)
constexpr (C++11)
consteval (C++20)
constinit (C++20)
Speicherdauer-Spezifizierer
Initialisierung
Ausdrücke
Alternative Darstellungen
Literale
Boolesch - Ganzzahl - Gleitkommazahl
Zeichen - String - nullptr (C++11)
Benutzerdefinierte (C++11)
Dienstprogramme
Attribute (C++11)
Typen
typedef-Deklaration
Typalias-Deklaration (C++11)
Umwandlungen
Speicherzuweisung
Klassen
Klassenspezifische Funktionseigenschaften
explicit (C++11)
static

Spezielle Member-Funktionen
Templates
Sonstiges
 
 
 
 

Um eine Klassenschablone zu instanziieren, muss jedes Schablonenargument bekannt sein, aber nicht jedes Schablonenargument muss angegeben werden. In den folgenden Kontexten leitet der Compiler die Schablonenargumente vom Typ des Initialisierers ab.

  • jede Deklaration, die die Initialisierung einer Variablen und einer Variablenschablone spezifiziert, deren deklarierter Typ die Klassenschablone ist (möglicherweise cv-qualifiziert)
std::pair p(2, 4.5);     // deduces to std::pair<int, double> p(2, 4.5);
std::tuple t(4, 3, 2.5); // same as auto t = std::make_tuple(4, 3, 2.5);
std::less l;             // same as std::less<void> l;
template<class T>
struct A
{
    A(T, T);
};
 
auto y = new A{1, 2}; // allocated type is A<int>
auto lck = std::lock_guard(mtx);     // deduces to std::lock_guard<std::mutex>
std::copy_n(vi1, 3,
    std::back_insert_iterator(vi2)); // deduces to std::back_insert_iterator<T>,
                                     // where T is the type of the container vi2
std::for_each(vi.begin(), vi.end(),
    Foo([&](int i) {...}));          // deduces to Foo<T>,
                                     // where T is the unique lambda type
template<class T>
struct X
{
    constexpr X(T) {}
};
 
template<X x>
struct Y {};
 
Y<0> y; // OK, Y<X<int>(0)>
(seit C++20)

Inhalt

[bearbeiten] Ableitung für Klassenschablonen

[bearbeiten] Implizit generierte Ableitungsführer

Wenn in einem Funktionsstil-Cast oder in der Deklaration einer Variablen der Typbezeichner ausschließlich aus dem Namen einer primären Klassenschablone C besteht (d. h. keine begleitende Schablonenargumentliste vorhanden ist), werden Kandidaten für die Ableitung wie folgt gebildet:

  • Wenn C definiert ist, wird für jeden Konstruktor (oder Konstruktorschablone) Ci, der in der benannten primären Schablone deklariert ist, eine fiktive Funktion-Schablone Fi konstruiert, so dass alle folgenden Bedingungen erfüllt sind:
  • Die Schablonenparameter von Fi sind die Schablonenparameter von C, gefolgt (falls Ci eine Konstruktorschablone ist) von den Schablonenparametern von Ci (Standard-Schablonenargumente sind ebenfalls enthalten).
  • Die zugehörigen Constraints von Fi sind die Konjunktion der zugehörigen Constraints von C und der zugehörigen Constraints von Ci.
(seit C++20)
  • Die Parameterliste von Fi ist die Parameterliste von Ci.
  • Der Rückgabetyp von Fi ist C, gefolgt von den Schablonenparametern der Klassenschablone, eingeschlossen in <>.
  • Wenn C nicht definiert ist oder keine Konstruktoren deklariert, wird eine zusätzliche fiktive Funktion-Schablone hinzugefügt, die wie oben aus einem hypothetischen Konstruktor C() abgeleitet ist.
  • In jedem Fall wird eine zusätzliche fiktive Funktion-Schablone hinzugefügt, die wie oben aus einem hypothetischen Konstruktor C(C) abgeleitet ist; diese wird als Kopierableitungskandidat bezeichnet.
  • Die Parameterliste von Fi ist die Parameterliste von Gi.
  • Der Rückgabetyp von Fi ist der einfache Schablonenbezeichner von Gi.
  • Wenn Gi Schablonenparameter hat (Syntax (2)), ist Fi eine Funktion-Schablone, und ihre Schablonenparameterliste ist die Schablonenparameterliste von Gi. Andernfalls ist Fi eine Funktion.
  • Zusätzlich, wenn
  • C definiert ist und die Anforderungen an einen aggregierten Typ erfüllt, unter der Annahme, dass jede abhängige Basisklasse keine virtuellen Funktionen oder virtuellen Basisklassen hat,
  • keine benutzerdefinierten Ableitungsführer für C vorhanden sind, und
  • die Variable aus einer nicht-leeren Liste von Initialisierern arg1, arg2, ..., argn initialisiert wird (die benannte Initialisierer verwenden kann),
kann ein Aggregat-Ableitungskandidat hinzugefügt werden. Die Parameterliste des Aggregat-Ableitungskandidaten wird aus den Elementtypen des Aggregats wie folgt gebildet:
  • Sei ei das (möglicherweise rekursive) Aggregat-Element, das aus argi initialisiert würde, wobei
  • wenn C (oder sein Element, das selbst ein Aggregat ist) eine Basis hat, die eine Parametererweiterung ist
  • wenn die Parametererweiterung ein nachgestelltes Aggregat-Element ist, wird sie als passend für alle verbleibenden Elemente der Initialisierungsliste betrachtet;
  • andernfalls wird die Parametererweiterung als leer betrachtet.
  • Wenn kein solches ei vorhanden ist, wird der Aggregat-Ableitungskandidat nicht hinzugefügt.
  • Andernfalls wird die Parameterliste T1, T2, ..., Tn des Aggregat-Ableitungskandidaten wie folgt bestimmt:
  • Wenn ei ein Array ist und argi eine braced-init-list ist, ist Ti eine rvalue-Referenz auf den deklarierten Typ von ei.
  • Wenn ei ein Array ist und argi ein Zeichenkettenliteral ist, ist Ti eine lvalue-Referenz auf den const-qualifizierten deklarierten Typ von ei.
  • Andernfalls ist Ti der deklarierte Typ von ei.
  • Wenn eine Parametererweiterung übersprungen wurde, da sie ein nicht nachgestelltes Aggregat-Element ist, wird eine zusätzliche Parametererweiterung der Form Pj ... an ihrer ursprünglichen Aggregat-Elementposition eingefügt. (Dies führt im Allgemeinen dazu, dass die Ableitung fehlschlägt.)
  • Wenn eine Parametererweiterung ein nachgestelltes Aggregat-Element ist, wird die nachgestellte Sequenz von Parametern, die ihr entspricht, durch einen einzelnen Parameter der Form Tn ... ersetzt.
Der Aggregat-Ableitungskandidat ist eine fiktive Funktion-Schablone, die wie oben aus einem hypothetischen Konstruktor C(T1, T2, ..., Tn) abgeleitet ist.
Während der Schablonen-Argumentableitung für den Aggregat-Ableitungskandidaten wird die Anzahl der Elemente in einer nachgestellten Parametererweiterung nur aus der Anzahl der verbleibenden Funktionsargumente abgeleitet, wenn sie nicht anderweitig abgeleitet wurde.
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)

Schablonen-Argumentableitung und Überladungsauflösung wird dann für die Initialisierung eines fiktiven Objekts eines hypothetischen Klassentyps durchgeführt, dessen Konstruktorsignaturen mit den Führern übereinstimmen (außer für den Rückgabetyp), zu dem Zweck, eine Überladungssammlung zu bilden. Der Initialisierer wird durch den Kontext bereitgestellt, in dem die Klassenschablonen-Argumentableitung durchgeführt wurde, mit der Ausnahme, dass die erste Phase der Listeninitialisierung (unter Berücksichtigung von Initialisierungslistenkonstruktoren) 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 Member des hypothetischen Klassentyps. Sie sind explizit, wenn der Führer 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-Schablonenspezialisierung zur abgeleiteten Klassenschablonenspezialisierung.

template<class T>
struct UniquePtr
{
    UniquePtr(T* t);
};
 
UniquePtr dp{new auto(2.0)};
 
// One declared constructor:
// C1: UniquePtr(T*);
 
// Set of implicitly-generated deduction guides:
 
// F1: template<class T>
//     UniquePtr<T> F(T* p);
 
// F2: template<class T> 
//     UniquePtr<T> F(UniquePtr<T>); // copy deduction candidate
 
// imaginary class to initialize:
// struct X
// {
//     template<class T>
//     X(T* p);         // from F1
//     
//     template<class T>
//     X(UniquePtr<T>); // from F2
// };
 
// direct-initialization of an X object
// with "new double(2.0)" as the initializer
// selects the constructor that corresponds to the guide F1 with T = double
// For F1 with T=double, the return type is UniquePtr<double>
 
// result:
// UniquePtr<double> dp{new auto(2.0)}

Oder, für ein komplexeres Beispiel (Hinweis: "S::N" würde nicht kompilieren: Bereichsauflösungsqualifizierer sind nichts, das 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};
 
// the implicitly-generated deduction guides are (note that T is already known to be 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>); (copy deduction candidate)
 
// Overload resolution for direct-list-init with "{2.0, 1}" as the initializer
// chooses F3 with U=int and V=double.
// The return type is S<int>::N<int>
 
// result:
// S<int>::N<int> x{2.0, 1};

[bearbeiten] Benutzerdefinierte Ableitungsführer

Die Syntax eines benutzerdefinierten Ableitungsführers ist die Syntax einer Funktions- (oder Funktionsschablonen-) Deklaration mit einem nachgestellten Rückgabetyp, außer dass sie den Namen einer Klassenschablone 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, durch Kommata getrennte Liste von Schablonenparametern
explicit - ein explicit Spezifizierer
template-name - der Name der Klassenschablone, deren Argumente abgeleitet werden sollen
parameter-liste - eine (möglicherweise leere) Parameterliste
simple-template-id - ein einfacher Schablonenbezeichner
requires-clause - (seit C++20) eine requires Klausel


Die Parameter von benutzerdefinierten Ableitungsführern dürfen keine Platzhaltertypen haben: die Syntax für verkürzte Funktion-Schablonen ist nicht erlaubt.

(seit C++20)

Benutzerdefinierte Ableitungsführer müssen eine Klassenschablone benennen und müssen innerhalb desselben semantischen Gültigkeitsbereichs der Klassenschablone (was ein Namespace oder eine einschließende Klasse sein kann) eingeführt werden. Für eine Member-Klassenschablone müssen sie denselben Zugriff haben, aber Ableitungsführer werden keine Member dieses Gültigkeitsbereichs.

Ein Ableitungsführer ist keine Funktion und hat keinen Körper. Ableitungsführer werden nicht durch Namenssuche gefunden und nehmen nicht an der Überladungsauflösung teil, außer für die Überladungsauflösung mit anderen Ableitungsführern bei der Ableitung von Klassenschablonenargumenten. Ableitungsführer können nicht in derselben Übersetzungseinheit für dieselbe Klassenschablone neu deklariert werden.

// declaration of the template
template<class T>
struct container
{
    container(T t) {}
 
    template<class Iter>
    container(Iter beg, Iter end);
};
 
// additional deduction guide
template<class Iter>
container(Iter b, Iter e) -> container<typename std::iterator_traits<Iter>::value_type>;
 
// uses
container c(7); // OK: deduces T=int using an implicitly-generated guide
std::vector<double> v = {/* ... */};
auto d = container(v.begin(), v.end()); // OK: deduces T=double
container e{5, 6}; // Error: there is no std::iterator_traits<int>::value_type

Die fiktiven Konstruktoren für den Zweck der Überladungsauflösung (wie oben beschrieben) sind explizit, wenn sie einem implizit generierten Ableitungsführer entsprechen, der aus einem expliziten Konstruktor gebildet wurde, oder einem benutzerdefinierten Ableitungsführer, der explizit deklariert wurde. Wie immer werden solche Konstruktoren im Kopierinitialisierungskontext ignoriert.

template<class T>
struct A
{
    explicit A(const T&, ...) noexcept; // #1
    A(T&&, ...);                        // #2
};
 
int i;
A a1 = {i, i}; // error: cannot deduce from rvalue reference in #2,
               // and #1 is explicit, and not considered in copy-initialization.
A a2{i, i};    // OK, #1 deduces to A<int> and also initializes
A a3{0, i};    // OK, #2 deduces to A<int> and also initializes
A a4 = {0, i}; // OK, #2 deduces to A<int> and also initializes
 
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}; // error: #3 deduces to A<int&>
               // and #1 & #2 result in same parameter constructors.
A a6{0, 1};    // OK, #4 deduces to A<int> and #2 initializes
A a7 = {0, i}; // error: #3 deduces to A<int&>
A a8{0, i};    // error: #3 deduces to A<int&>
 
// Note: check https://github.com/cplusplus/CWG/issues/647, claiming that
// examples a7 and a8 are incorrect, to be possibly replaced as
//A a7 = {0, i}; // error: #2 and #3 both match, overload resolution fails
//A a8{i,i};     // error: #3 deduces to A<int&>,
//               //        #1 and #2 declare same constructor

Die Verwendung eines Member-Typedefs oder einer Alias-Schablone in der Parameterliste eines Konstruktors oder einer Konstruktorschablone macht den entsprechenden Parameter des implizit generierten Führers nicht von sich aus zu einem nicht-abgeleiteten Kontext.

template<class T>
struct B
{
    template<class U>
    using TA = T;
 
    template<class U>
    B(U, TA<U>); // #1
};
 
// Implicit deduction guide generated from #1 is the equivalent of
//     template<class T, class U>
//     B(U, T) -> B<T>;
// rather than
//     template<class T, class U>
//     B(U, typename B<T>::template TA<U>) -> B<T>;
// which would not have been deducible
 
B b{(int*)0, (char*)0}; // OK, deduces B<char*>

Ableitung für Alias-Schablonen

Wenn ein Funktionsstil-Cast oder eine Variablendeklaration den Namen einer Alias-Schablone A ohne Argumentliste als Typbezeichner verwendet, wobei A als Alias für B<ArgList> definiert ist, der Gültigkeitsbereich von B nicht-abhängig ist und B entweder eine Klassenschablone oder eine ähnlich definierte Alias-Schablone ist, erfolgt die Ableitung auf dieselbe Weise wie für Klassenschablonen, außer dass die Führer stattdessen aus den Führern von B generiert werden, wie folgt:

  • Für jeden Führer f von B werden die Schablonenargumente des Rückgabetyps von f aus B<ArgList> unter Verwendung von Schablonen-Argumentableitung abgeleitet, mit der Ausnahme, dass die Ableitung nicht fehlschlägt, wenn einige Argumente nicht abgeleitet werden. Wenn die Ableitung aus einem anderen Grund fehlschlägt, wird mit einer leeren Menge abgeleiteter Schablonenargumente fortgefahren.
  • Ersetzen Sie das Ergebnis der obigen Ableitung in f. Wenn die Ersetzung fehlschlägt, wird kein Führer erzeugt; andernfalls, sei g das Ergebnis der Ersetzung, wird ein Führer f' gebildet, so dass
  • Die Parametertypen und der Rückgabetyp von f' sind dieselben wie bei g
  • Wenn f eine Schablone ist, ist f' eine Funktion-Schablone, deren Schablonenparameterliste aus allen Schablonenparametern von A (einschließlich ihrer Standard-Schablonenargumente) besteht, die in den obigen Ableitungen oder (rekursiv) in ihren Standard-Schablonenargumenten vorkommen, gefolgt von den Schablonenparametern von f, die nicht abgeleitet wurden (einschließlich ihrer Standard-Schablonenargumente); andernfalls (wenn f keine Schablone ist) ist f' eine Funktion.
  • Die zugehörigen Constraints von f' sind die Konjunktion der zugehörigen Constraints von g und eines Constraints, der genau dann erfüllt ist, wenn die Argumente von A aus dem Ergebnistyp abgeleitet werden können.
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[]>;
 
// generated guide for unique_ptr_nonarray:
 
// from #1 (deduction of unique_ptr<T> from unique_ptr<A> yields T = A):
// template<class A>
//     requires(argument_of_unique_ptr_nonarray_is_deducible_from<unique_ptr<A>>)
// auto F(A*) -> unique_ptr<A>;
 
// from #2 (deduction of unique_ptr<T[]> from unique_ptr<A> yields nothing):
// template<class T>
//     requires(argument_of_unique_ptr_nonarray_is_deducible_from<unique_ptr<T[]>>)
// auto F(T*) -> unique_ptr<T[]>;
 
// where argument_of_unique_ptr_nonarray_is_deducible_from can be defined as
 
// 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>); };
 
// generated guide for unique_ptr_array:
 
// from #1 (deduction of unique_ptr<T> from unique_ptr<A[]> yields T = A[]):
// template<class A>
//     requires(argument_of_unique_ptr_array_is_deducible_from<unique_ptr<A[]>>)
// auto F(A(*)[]) -> unique_ptr<A[]>;
 
// from #2 (deduction of unique_ptr<T[]> from unique_ptr<A[]> yields T = A):
// template<class A>
//     requires(argument_of_unique_ptr_array_is_deducible_from<unique_ptr<A[]>>)
// auto F(A*) -> unique_ptr<A[]>;
 
// where argument_of_unique_ptr_array_is_deducible_from can be defined as
 
// 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>); };
 
// Use:
unique_ptr_nonarray p(new int); // deduced to unique_ptr<int>
// deduction guide generated from #1 returns unique_ptr<int>
// deduction guide generated from #2 returns unique_ptr<int[]>, which is ignored because
//   argument_of_unique_ptr_nonarray_is_deducible_from<unique_ptr<int[]>> is unsatisfied
 
unique_ptr_array q(new int[42]); // deduced to unique_ptr<int[]>
// deduction guide generated from #1 fails (cannot deduce A in A(*)[] from new int[42])
// deduction guide generated from #2 returns unique_ptr<int[]>
(seit C++20)

[bearbeiten] Anmerkungen

Die Klassenschablonen-Argumentableitung wird nur dann durchgeführt, wenn keine Schablonenargumentliste vorhanden ist. Wenn eine Schablonenargumentliste angegeben ist, findet keine Ableitung statt.

std::tuple t1(1, 2, 3);                // OK: deduction
std::tuple<int, int, int> t2(1, 2, 3); // OK: all arguments are provided
 
std::tuple<> t3(1, 2, 3);    // Error: no matching constructor in tuple<>.
                             //        No deduction performed.
std::tuple<int> t4(1, 2, 3); // Error

Die Klassenschablonen-Argumentableitung von Aggregaten erfordert typischerweise benutzerdefinierte Ableitungsführer.

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 Ableitungsführer müssen keine Schablonen sein.

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

Innerhalb des Gültigkeitsbereichs einer Klassenschablone ist der Name der Schablone ohne Parameterliste ein injizierter Klassenname und kann als Typ verwendet werden. In diesem Fall findet keine Klassenschablonen-Argumentableitung statt und Schablonenparameter 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); // no deduction: X is the current X<T>
    }
 
    template<class Iter>
    auto bar(Iter b, Iter e)
    {
        return X<typename Iter::value_type>(b, e); // must specify what we want
    }
 
    auto baz()
    {
        return ::X(0); // not the injected-class-name; deduced to be X<int>
    }
};

Bei der Überladungsauflösung hat die Teilordnung Vorrang vor der Frage, ob eine Funktion-Schablone aus einem benutzerdefinierten Ableitungsführer generiert wurde: Wenn die von einem Konstruktor generierte Funktion-Schablone spezialisierter ist als die vom benutzerdefinierten Ableitungsführer generierte, wird die vom Konstruktor generierte gewählt. Da der Kopierableitungskandidat typischerweise spezialisierter ist als ein umhüllender Konstruktor, bedeutet diese Regel, dass das Kopieren im Allgemeinen dem Umhüllen 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}; // uses #1 to deduce A<int> and initializes with #1
A b{a, 0}; // uses #2 (more specialized than #3) to deduce A<int> and initializes with #2

Wenn frühere Auswahlkriterien, einschließlich Teilordnung, zwei Kandidaten-Funktionsschablonen nicht unterscheiden konnten, gelten die folgenden Regeln:

  • Eine von einem benutzerdefinierten Ableitungsführer generierte Funktion-Schablone wird gegenüber einer implizit aus einem Konstruktor oder einer Konstruktorschablone generierten bevorzugt.
  • Der Kopierableitungskandidat wird allen anderen implizit aus einem Konstruktor oder einer Konstruktorschablone generierten Funktion-Schablonen vorgezogen.
  • Eine implizit aus einem Nicht-Schablonenkonstruktor generierte Funktion-Schablone wird gegenüber einer implizit aus einer Konstruktorschablone generierten Funktion-Schablone 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, the copy deduction candidate A(A);
 
A x(1, 2, 3); // uses #3, generated from a non-template constructor
 
template<class T>
A(T) -> A<T>; // #6, less specialized than #5
 
A a(42); // uses #6 to deduce A<int> and #1 to initialize
A b = a; // uses #5 to deduce A<int> and #2 to initialize
 
template<class T>
A(A<T>) -> A<A<T>>; // #7, as specialized as #5
 
A b2 = a; // uses #7 to deduce A<A<int>> and #1 to initialize

Eine rvalue-Referenz auf einen cv-unqualifizierten Schablonenparameter ist keine weiterleitende Referenz, wenn dieser Parameter ein Klassenschablonenparameter ist.

template<class T>
struct A
{
    template<class U>
    A(T&&, U&&, int*); // #1: T&& is not a forwarding reference
                       //     U&& is a forwarding reference
 
    A(T&&, int*);      // #2: T&& is not a forwarding reference
};
 
template<class T>
A(T&&, int*) -> A<T>; // #3: T&& is a forwarding reference
 
int i, *ip;
A a{i, 0, ip};  // error, cannot deduce from #1
A a0{0, 0, ip}; // uses #1 to deduce A<int> and #1 to initialize
A a2{i, ip};    // uses #3 to deduce A<int&> and #2 to initialize

Bei der Initialisierung aus einem einzelnen Argument eines Typs, der eine Spezialisierung der betreffenden Klassenschablone ist, wird die Kopierableitung standardmäßig im Allgemeinen der Umhüllung vorgezogen.

std::tuple t1{1};  //std::tuple<int>
std::tuple t2{t1}; //std::tuple<int>, not std::tuple<std::tuple<int>>
 
std::vector v1{1, 2};   // std::vector<int>
std::vector v2{v1};     // std::vector<int>, not 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 Bevorzugung von Initialisierungslistenkonstruktoren bei der Listeninitialisierung bestehen.

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>

Bevor die Klassenschablonen-Argumentableitung eingeführt wurde, war die Verwendung einer Funktion-Schablone ein gängiger Ansatz, um die explizite Angabe von Argumenten zu vermeiden.

std::tuple p1{1, 1.0};             //std::tuple<int, double>, using deduction
auto p2 = std::make_tuple(1, 1.0); //std::tuple<int, double>, pre-C++17
Feature-Testmakro Wert Std Feature
__cpp_deduction_guides 201703L (C++17) Schablonen-Argumentableitung für Klassenschablonen
201907L (C++20) CTAD für Aggregate und Aliase

[bearbeiten] Fehlerberichte

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

DR angewendet auf Verhalten wie veröffentlicht Korrigiertes Verhalten
CWG 2376 C++17 CTAD würde auch dann durchgeführt, wenn der Typ der deklarierten Variablen
sich von der Klassenschablone unterscheidet, deren Argumente abgeleitet werden sollen
führt keine durch
CTAD in diesem Fall durch
CWG 2628 C++20 implizite Ableitungsführer propagierten keine Constraints propagiert Constraints
CWG 2697 C++20 war unklar, ob die verkürzte Funktion-Schablonen-
Syntax in benutzerdefinierten Ableitungsführern erlaubt ist
verboten
CWG 2707 C++20 Ableitungsführer konnten keine nachgestellte requires Klausel haben sie können es
CWG 2714 C++17 implizite Ableitungsführer berücksichtigten
die Standardargumente von Konstruktoren nicht
berücksichtigt sie
CWG 2913 C++20 die Auflösung von CWG-Problem 2707 machte die Syntax von Ableitungsführern
inkonsistent mit der Syntax von Funktionsdeklarationen
passt die Syntax an
P0702R1 C++17 ein Initialisierungslistenkonstruktor kann den
Kopierableitungskandidaten übertrumpfen, was zu Umhüllung führt
Initialisierungslistenphase
wird beim Kopieren übersprungen