Template argument deduction
Um eine function template zu instanziieren, müssen alle Template-Argumente bekannt sein, aber nicht jedes Template-Argument muss angegeben werden. Wenn möglich, leitet der Compiler die fehlenden Template-Argumente aus den Funktionsargumenten ab. Dies geschieht, wenn ein Funktionsaufruf versucht wird, wenn die Adresse einer Funktionsvorlage genommen wird und in einigen other contexts :
template<typename To, typename From> To convert(From f); void g(double d) { int i = convert<int>(d); // ruft convert<int, double>(double) auf char c = convert<char>(d); // ruft convert<char, double>(double) auf int(*ptr)(float) = convert; // instanziiert convert<int, float>(float) // und speichert seine Adresse in ptr }
Dieser Mechanismus ermöglicht die Verwendung von Template-Operatoren, da es keine Syntax gibt, um Template-Argumente für einen Operator anzugeben, außer durch Umschreiben als Funktionsaufrufausdruck:
Die Template-Argument-Deduktion findet statt nach der Namenssuche für Funktions-Templates (die eine argumentabhängige Suche beinhalten kann) und vor der Template-Argument-Substitution (die SFINAE beinhalten kann) und der Überladungsauflösung .
|
Template-Argument-Deduktion wird auch durchgeführt, wenn der Name eines Klassentemplates als Typ eines zu konstruierenden Objekts verwendet wird: std::pair p(2, 4.5); std::tuple t(4, 3, 2.5); std::copy_n(vi1, 3, std::back_insert_iterator(vi2)); std::for_each(vi.begin(), vi.end(), Foo([&](int i) {...})); auto lck = std::lock_guard(foo.mtx); std::lock_guard lck2(foo.mtx, ul); Template-Argument-Deduktion für Klassentemplates findet in Deklarationen und in expliziten Cast-Ausdrücken statt; siehe Klassentemplate-Argument-Deduktion für Details. |
(seit C++17) |
Inhaltsverzeichnis |
Ableitung von einem Funktionsaufruf
Template-Argument-Deduktion versucht, Template-Argumente zu bestimmen (Typen für Typ-Template-Parameter
T
, Templates für Template-Template-Parameter
TT
und Werte für konstante Template-Parameter
I
), die in jeden Parameter
P
eingesetzt werden können, um den
deduzierten
Typ
A
zu erzeugen, der nach den unten aufgeführten Anpassungen dem Typ des Arguments
A
entspricht.
Wenn mehrere Parameter vorhanden sind, wird jedes
P
/
A
-Paar separat abgeleitet und die abgeleiteten Template-Argumente werden anschließend kombiniert. Wenn die Ableitung für ein
P
/
A
-Paar fehlschlägt oder mehrdeutig ist, oder wenn verschiedene Paare unterschiedliche abgeleitete Template-Argumente ergeben, oder wenn ein Template-Argument weder abgeleitet noch explizit angegeben wird, schlägt die Kompilierung fehl.
|
Wenn das Entfernen von Referenzen und CV-Qualifizierern von
template<class T> void f(std::initializer_list<T>); f({1, 2, 3}); // P = std::initializer_list<T>, A = {1, 2, 3} // P'1 = T, A'1 = 1: deduced T = int // P'2 = T, A'2 = 2: deduced T = int // P'3 = T, A'3 = 3: deduced T = int // OK: deduced T = int f({1, "abc"}); // P = std::initializer_list<T>, A = {1, "abc"} // P'1 = T, A'1 = 1: deduced T = int // P'2 = T, A'2 = "abc": deduced T = const char* // error: deduction fails, T is ambiguous
Wenn das Entfernen von Referenzen und CV-Qualifizierern von
template<class T, int N> void h(T const(&)[N]); h({1, 2, 3}); // deduced T = int, deduced N = 3 template<class T> void j(T const(&)[3]); j({42}); // deduced T = int, array bound is not a parameter, not considered struct Aggr { int i; int j; }; template<int N> void k(Aggr const(&)[N]); k({1, 2, 3}); // error: deduction fails, no conversion from int to Aggr k({{1}, {2}, {3}}); // OK: deduced N = 3 template<int M, int N> void m(int const(&)[M][N]); m({{1, 2}, {3, 4}}); // deduced M = 2, deduced N = 2 template<class T, int N> void n(T const(&)[N], T); n({{1}, {2}, {3}}, Aggr()); // deduced T = Aggr, deduced N = 3
Wenn ein
parameter pack
als letztes
template<class... Types> void f(Types&...); void h(int x, float& y) { const int z = x; f(x, y, z); // P = Types&..., A1 = x: deduced first member of Types... = int // P = Types&..., A2 = y: deduced second member of Types... = float // P = Types&..., A3 = z: deduced third member of Types... = const int // calls f<int, float, const int> }
|
(seit C++11) |
Wenn
P
ein Funktionstyp, Zeiger auf Funktionstyp oder Zeiger auf Memberfunktionstyp ist und wenn
A
ein
Satz überladener Funktionen
ist, der keine Funktions-Templates enthält, wird Template-Argument-Deduktion mit jeder Überladung versucht. Wenn nur eine erfolgreich ist, wird diese erfolgreiche Deduktion verwendet. Wenn keine oder mehr als eine erfolgreich ist, ist der Template-Parameter ein nicht-deduzierter Kontext (siehe unten):
template<class T> int f(T(*p)(T)); int g(int); int g(char); f(g); // P = T(*)(T), A = Überladungsmenge // P = T(*)(T), A1 = int(int): T = int abgeleitet // P = T(*)(T), A2 = int(char): T-Ableitung fehlgeschlagen // nur eine Überladung funktioniert, Ableitung erfolgreich
Vor Beginn der Abzugsoperation werden die folgenden Anpassungen an
P
und
A
vorgenommen:
P
kein Referenztyp ist,
A
ein Array-Typ ist,
A
wird durch den Pointer-Typ ersetzt, der durch Array-zu-Pointer-Konvertierung erhalten wird;
A
ein Funktionstyp ist,
A
wird durch den Zeigertyp ersetzt, der durch die Funktion-zu-Zeiger-Konvertierung erhalten wird;
A
ein cv-qualifizierter Typ ist, werden die obersten CV-Qualifizierer für die Deduktion ignoriert:
template<class T> void f(T); int a[3]; f(a); // P = T, A = int[3], adjusted to int*: deduced T = int* void b(int); f(b); // P = T, A = void(int), adjusted to void(*)(int): deduced T = void(*)(int) const int c = 13; f(c); // P = T, A = const int, adjusted to int: deduced T = int
P
ein cv-qualifizierter Typ ist, werden die obersten CV-Qualifizierer für die Deduktion ignoriert.
P
ein Referenztyp ist, wird der referenzierte Typ für die Deduktion verwendet.
P
eine Rvalue-Referenz auf einen cv-unqualifizierten Template-Parameter ist (sogenannte
Forwarding References
) und das entsprechende Funktionsargument ein Lvalue ist, wird der Typ Lvalue-Referenz auf
A
anstelle von
A
für die Deduktion verwendet (Hinweis: Dies ist die Grundlage für die Wirkungsweise von
std::forward
. Hinweis: Bei der
Class Template Argument Deduction
ist der Template-Parameter einer Klassenvorlage niemals eine Forwarding Reference
(seit C++17)
):
template<class T> int f(T&&); // P is an rvalue reference to cv-unqualified T (forwarding reference) template<class T> int g(const T&&); // P is an rvalue reference to cv-qualified T (not special) int main() { int i; int n1 = f(i); // argument is lvalue: calls f<int&>(int&) (special case) int n2 = f(0); // argument is not lvalue: calls f<int>(int&&) // int n3 = g(i); // error: deduces to g<int>(const int&&), which // cannot bind an rvalue reference to an lvalue }
Nach diesen Transformationen läuft der Deduktionsprozess wie unten beschrieben ab (vgl. Abschnitt
Deduktion von einem Typ
) und versucht, solche Template-Argumente zu finden, die das deduzierte
A
(d.h.
P
nach den oben aufgeführten Anpassungen und der Substitution der deduzierten Template-Parameter) identisch zum
transformierten
A
machen würden, also
A
nach den oben aufgeführten Anpassungen.
Falls die übliche Deduktion von
P
und
A
fehlschlägt, werden folgende Alternativen zusätzlich in Betracht gezogen:
P
ein Referenztyp ist, kann das abgeleitete
A
(d.h. der Typ, auf den die Referenz verweist) stärker cv-qualifiziert sein als das transformierte
A
:
template<typename T> void f(const T& t); bool a = false; f(a); // P = const T&, adjusted to const T, A = bool: // deduced T = bool, deduced A = const bool // deduced A is more cv-qualified than A
A
kann ein anderer Zeiger oder Zeiger auf einen Membertyp sein, der über eine
Qualifikationskonvertierung
oder eine Funktionszeigerkonvertierung
(seit C++17)
in den abgeleiteten
A
konvertiert werden kann:
template<typename T> void f(const T*); int* p; f(p); // P = const T*, A = int*: // deduced T = int, deduced A = const int* // qualification conversion applies (from int* to const int*)
P
eine Klasse ist und
P
die Form
simple-template-id
hat, dann kann das transformierte
A
eine abgeleitete Klasse des abgeleiteten
A
sein. Ebenso, wenn
P
ein Zeiger auf eine Klasse der Form
simple-template-id
ist, kann das transformierte
A
ein Zeiger auf eine abgeleitete Klasse sein, auf die der abgeleitete
A
zeigt:
template<class T> struct B {}; template<class T> struct D : public B<T> {}; template<class T> void f(B<T>&) {} void f() { D<int> d; f(d); // P = B<T>&, adjusted to P = B<T> (a simple-template-id), A = D<int>: // deduced T = int, deduced A = B<int> // A is derived from deduced A }
Nicht abgeleitete Kontexte
In den folgenden Fällen nehmen die Typen, Templates und Konstanten, die zur Zusammensetzung von
P
verwendet werden, nicht an der Template-Argumentableitung teil, sondern
verwenden
die Template-Argumente, die entweder anderweitig abgeleitet oder explizit angegeben wurden. Wenn ein Template-Parameter nur in nicht abgeleiteten Kontexten verwendet wird und nicht explizit angegeben ist, schlägt die Template-Argumentableitung fehl.
// the identity template, often used to exclude specific arguments from deduction // (available as std::type_identity as of C++20) template<typename T> struct identity { typedef T type; }; template<typename T> void bad(std::vector<T> x, T value = 1); template<typename T> void good(std::vector<T> x, typename identity<T>::type value = 1); std::vector<std::complex<double>> x; bad(x, 1.2); // P1 = std::vector<T>, A1 = std::vector<std::complex<double>> // P1/A1: deduced T = std::complex<double> // P2 = T, A2 = double // P2/A2: deduced T = double // error: deduction fails, T is ambiguous good(x, 1.2); // P1 = std::vector<T>, A1 = std::vector<std::complex<double>> // P1/A1: deduced T = std::complex<double> // P2 = identity<T>::type, A2 = double // P2/A2: uses T deduced by P1/A1 because T is to the left of :: in P2 // OK: T = std::complex<double>
|
2)
Ein
Pack-Indexierungs-Spezifizierer
oder ein
Pack-Indexierungs-Ausdruck
:
template<typename... Ts> void f(Ts...[0], std::tuple<Ts...>); f(3, std::tuple(5, 'A')); // P2 = std::tuple<Ts...>, A2 = std::tuple<int, char> // P2/A2: deduced first member of Ts... = int // P2/A2: deduced second member of Ts... = char // P1 = Ts...[0], A1 = int: Ts...[0] is in non-deduced context |
(seit C++26) |
|
3)
Der Ausdruck eines
decltype
-Spezifizierers:
template<typename T> void f(decltype(*std::declval<T>()) arg); int n; f<int*>(n); // P = decltype(*declval<T>()), A = int: T is in non-deduced context |
(seit C++11) |
template<std::size_t N> void f(std::array<int, 2 * N> a); std::array<int, 10> a; f(a); // P = std::array<int, 2 * N>, A = std::array<int, 10>: // 2 * N is non-deduced context, N cannot be deduced // note: f(std::array<int, N> a) would be able to deduce N
template<typename T, typename F> void f(const std::vector<T>& v, const F& comp = std::less<T>()); std::vector<std::string> v(3); f(v); // P1 = const std::vector<T>&, A1 = std::vector<std::string> lvalue // P1/A1 deduced T = std::string // P2 = const F&, A2 = std::less<std::string> rvalue // P2 is non-deduced context for F (template parameter) used in the // parameter type (const F&) of the function parameter comp, // that has a default argument that is being used in the call f(v)
P
, dessen
A
eine Funktion oder eine Menge von Überladungen ist, sodass mehr als eine Funktion zu
P
passt oder keine Funktion zu
P
passt oder die Menge von Überladungen eine oder mehrere Funktionsvorlagen enthält:
P
, dessen
A
eine Brace-Initialisierungsliste ist, aber
P
ist nicht
std::initializer_list
, eine Referenz darauf (möglicherweise cv-qualifiziert) oder eine Referenz auf ein Array}}:
template<class T> void g1(std::vector<T>); template<class T> void g2(std::vector<T>, T x); g1({1, 2, 3}); // P = std::vector<T>, A = {1, 2, 3}: T ist im nicht-deduzierten Kontext // Fehler: T ist nicht explizit angegeben oder von einem anderen P/A deduziert g2({1, 2, 3}, 10); // P1 = std::vector<T>, A1 = {1, 2, 3}: T ist im nicht-deduzierten Kontext // P2 = T, A2 = int: deduziert T = int
|
8)
Der Parameter
P
, welcher ein Parameterpaket ist und nicht am Ende der Parameterliste steht:
template<class... Ts, class T> void f1(T n, Ts... args); template<class... Ts, class T> void f2(Ts... args, T n); f1(1, 2, 3, 4); // P1 = T, A1 = 1: deduced T = int // P2 = Ts..., A2 = 2, A3 = 3, A4 = 4: deduced Ts = [int, int, int] f2(1, 2, 3, 4); // P1 = Ts...: Ts is non-deduced context
9)
Die Template-Parameterliste, die innerhalb des Parameters
P
erscheint und eine Paketentwicklung enthält, die nicht ganz am Ende der Template-Parameterliste steht:
template<int...> struct T {}; template<int... Ts1, int N, int... Ts2> void good(const T<N, Ts1...>& arg1, const T<N, Ts2...>&); template<int... Ts1, int N, int... Ts2> void bad(const T<Ts1..., N>& arg1, const T<Ts2..., N>&); T<1, 2> t1; T<1, -1, 0> t2; good(t1, t2); // P1 = const T<N, Ts1...>&, A1 = T<1, 2>: // deduced N = 1, deduced Ts1 = [2] // P2 = const T<N, Ts2...>&, A2 = T<1, -1, 0>: // deduced N = 1, deduced Ts2 = [-1, 0] bad(t1, t2); // P1 = const T<Ts1..., N>&, A1 = T<1, 2>: // <Ts1..., N> is non-deduced context // P2 = const T<Ts2..., N>&, A2 = T<1, -1, 0>: // <Ts2..., N> is non-deduced context |
(seit C++11) |
P
vom Array-Typ (aber nicht Referenz auf Array oder Zeiger auf Array), die Haupt-Array-Grenze:
template<int i> void f1(int a[10][i]); template<int i> void f2(int a[i][20]); // P = int[i][20], array type template<int i> void f3(int (&a)[i][20]); // P = int(&)[i][20], reference to array void g() { int a[10][20]; f1(a); // OK: deduced i = 20 f1<20>(a); // OK f2(a); // error: i is non-deduced context f2<10>(a); // OK f3(a); // OK: deduced i = 10 f3<10>(a); // OK }
In jedem Fall, wenn irgendein Teil eines Typnamens nicht abgeleitet wird, ist der gesamte Typname ein nicht abgeleiteter Kontext. Jedoch können zusammengesetzte Typen sowohl abgeleitete als auch nicht abgeleitete Typnamen enthalten. Zum Beispiel, in
A
<
T
>
::
B
<
T2
>
,
T
ist nicht abgeleitet aufgrund von Regel #1 (geschachtelter Namensspezifizierer), und
T2
ist nicht abgeleitet, weil es Teil desselben Typnamens ist, aber in
void
(
*
f
)
(
typename
A
<
T
>
::
B
, A
<
T
>
)
, das
T
in
A
<
T
>
::
B
ist nicht abgeleitet (aufgrund derselben Regel), während das
T
in
A
<
T
>
abgeleitet wird.
Ableitung von einem Typ
Gegeben einen Funktionsparameter
P
, der von einem oder mehreren Typ-Template-Parametern
T
i, Template-Template-Parametern
TT
i oder konstanten Template-Parametern
I
i abhängt, und dem entsprechenden Argument
A
, findet Deduktion statt, wenn
P
eine der folgenden Formen hat:
|
Dieser Abschnitt ist unvollständig
Grund: möglicherweise eine Tabelle mit Mikro-Beispielen |
-
cv(optional)T; -
T*; -
T&;
|
(seit C++11) |
-
T(optional)[I(optional)];
|
(bis C++17) |
|
(seit C++17) |
-
T(optional)U(optional)::*; -
TT(optional)<T>; -
TT(optional)<I>; -
TT(optional)<TU>; -
TT(optional)<>.
In den obigen Formen,
-
T(optional) oderU(optional) repräsentiert einen Typ oder eine parameter-type-list , die entweder diese Regeln rekursiv erfüllt, ein nicht abgeleiteter Kontext inPoderAist, oder der gleiche nicht-abhängige Typ inPundAist. -
TT(optional) oderTU(optional) repräsentiert entweder eine Klassentemplate oder einen Template-Template-Parameter. -
I(optional) repräsentiert einen Ausdruck, der entweder einIist, wertabhängig inPoderAist, oder den gleichen konstanten Wert inPundAhat.
|
(seit C++17) |
Wenn
P
eine der Formen hat, die eine Template-Parameterliste
<T>
oder
<I>
einschließt, dann wird jedes Element
P
i dieser Template-Argumentliste mit dem entsprechenden Template-Argument
A
i seines
A
abgeglichen. Wenn das letzte
P
i eine Pack-Expansion ist, dann wird sein Muster mit jedem verbleibenden Argument in der Template-Argumentliste von
A
verglichen. Ein nachfolgender Parameter-Pack, der nicht anderweitig deduziert wird, wird zu einem leeren Parameter-Pack deduziert.
Wenn
P
eine der Formen hat, die eine Funktionsparameterliste
(T)
einschließt, dann wird jeder Parameter
P
i aus dieser Liste mit dem entsprechenden Argument
A
i aus der Funktionsparameterliste von
A
verglichen. Wenn der letzte
P
i eine Pack-Expansion ist, dann wird sein Deklarator mit jedem verbleibenden
A
i in der Parametertypenliste von
A
verglichen.
Formulare können geschachtelt und rekursiv verarbeitet werden:
-
X
<
int
>
(
*
)
(
char
[
6
]
)
ist ein Beispiel für
T*, wobeiTgleich X < int > ( char [ 6 ] ) ist;
|
(bis C++17) |
|
(seit C++17) |
-
X
<
int
>
ist ein Beispiel für
TT(optional)<T>, wobeiTTXundTint ist, und -
char
[
6
]
ist ein Beispiel für
T(optional)[I(optional)], wobeiTchar undIstd:: size_t ( 6 ) ist.
|
Typ-Template-Argument kann nicht vom Typ eines konstanten Template-Arguments abgeleitet werden: template<typename T, T i> void f(double a[10][i]); double v[10][20]; f(v); // P = double[10][i], A = double[10][20]: // i kann zu 20 abgeleitet werden // aber T kann nicht vom Typ von i abgeleitet werden |
(bis C++17) |
|
Wenn der Wert des Arguments, das einem konstanten Template-Parameter P mit abhängigem Typ entspricht, aus einem Ausdruck abgeleitet wird, werden die Template-Parameter im Typ von P aus dem Typ des Werts abgeleitet. template<long n> struct A {}; template<class T> struct C; template<class T, T n> struct C<A<n>> { using Q = T; }; typedef long R; typedef C<A<2>>::Q R; // OK: T wurde zu long abgeleitet // aus dem Template-Argumentwert im Typ A<2> template<auto X> class bar {}; template<class T, T n> void f(bar<n> x); f(bar<3>{}); // OK: T wurde zu int abgeleitet (und n zu 3) // aus dem Template-Argumentwert im Typ bar<3>
Der Typ von
template<class T, T i> void f(int (&a)[i]); int v[10]; f(v); // OK: T ist std::size_t
Der Typ von
template<bool> struct A {}; template<auto> struct B; template<auto X, void (*F)() noexcept(X)> struct B<F> { A<X> ax; }; void f_nothrow() noexcept; B<f_nothrow> bn; // OK: X wird als true abgeleitet und der Typ von X wird als bool abgeleitet. |
(seit C++17) |
Wenn ein konstanter Template-Parameter einer Funktionsvorlage in der Template-Parameterliste eines Funktionsparameters (der ebenfalls ein Template ist) verwendet wird und das entsprechende Template-Argument abgeleitet wird, muss der Typ des abgeleiteten Template-Arguments (wie in seiner umschließenden Template-Parameterliste angegeben, was bedeutet, dass Referenzen erhalten bleiben) exakt mit dem Typ des konstanten Template-Parameters übereinstimmen, außer dass CV-Qualifizierer entfernt werden, und außer wenn das Template-Argument von einer Array-Grenze abgeleitet wird – in diesem Fall ist jeder integrale Typ erlaubt, selbst bool obwohl es immer zu true werden würde:
template<int i> class A {}; template<short s> void f(A<s>); // der Typ des konstanten Template-Parameters ist short void k1() { A<1> a; // der Typ des konstanten Template-Parameters von a ist int f(a); // P = A<(short)s>, A = A<(int)1> // Fehler: abgeleiteter konstanter Template-Argument hat nicht denselben // Typ wie sein entsprechendes Template-Argument f<1>(a); // OK: das Template-Argument wird nicht abgeleitet, // dies ruft f<(short)1>(A<(short)1>) auf } template<int&> struct X; template<int& R> void k2(X<R>&); int n; void g(X<n> &x) { k2(x); // P = X<R>, A = X<n> // Parametertyp ist int& // Argumenttyp ist int& in der Template-Deklaration von struct X // OK (mit CWG 2091): leitet R ab, um auf n zu referenzieren }
Typ-Template-Parameter kann nicht vom Typ eines Funktionsstandardarguments abgeleitet werden:
template<typename T> void f(T = 5, T = 7); void g() { f(1); // OK: ruft f<int>(1, 7) auf f(); // Fehler: T kann nicht abgeleitet werden f<int>(); // OK: ruft f<int>(5, 7) auf }
Die Ableitung von Template-Template-Parametern kann den Typ verwenden, der in der Template-Spezialisierung verwendet wird, die im Funktionsaufruf verwendet wird:
template<template<typename> class X> struct A {}; // A ist eine Template mit einem TT-Parameter template<template<typename> class TT> void f(A<TT>) {} template<class T> struct B {}; A<B> ab; f(ab); // P = A<TT>, A = A<B>: abgeleitet TT = B, ruft f(A<B>) auf
Andere Kontexte
Neben Funktionsaufrufen und Operatorausdrücken wird die Template-Argument-Deduktion in den folgenden Situationen verwendet:
Automatische TypableitungDie Template-Argumentableitung wird in Deklarationen von Variablen verwendet, wenn die Bedeutung des auto-Spezifizierers aus dem Initialisierer der Variable abgeleitet wird.
Der Parameter
const auto& x = 1 + 2; // P = const U&, A = 1 + 2: // dieselben Regeln wie für den Aufruf f(1 + 2), wobei f // template<class U> void f(const U& u) ist // abgeleitetes U = int, der Typ von x ist const int& auto l = {13}; // P = std::initializer_list<U>, A = {13}: // abgeleitetes U = int, der Typ von l ist std::initializer_list<int> Bei der Direkt-List-Initialisierung (jedoch nicht bei der Copy-List-Initialisierung), wenn die Bedeutung von auto aus einer Initialisierungsliste in geschweiften Klammern abgeleitet wird, muss die Initialisierungsliste nur ein Element enthalten, und der Typ von auto wird der Typ dieses Elements sein: auto x1 = {3}; // x1 ist std::initializer_list<int> auto x2{1, 2}; // Fehler: kein einzelnes Element auto x3{3}; // x3 ist int // (vor N3922 waren x2 und x3 beide std::initializer_list<int>) |
(seit C++11) |
Auto-returning-FunktionenTemplate-Argument-Deduktion wird in Deklarationen von Funktionen verwendet, wenn die Bedeutung des auto -Spezifizierers im Rückgabetyp der Funktion aus der return-Anweisung abgeleitet wird.
Für Auto-returning-Funktionen wird der Parameter
auto f() { return 42; } // P = auto, A = 42: // deduced U = int, the return type of f is int Wenn eine solche Funktion mehrere return-Anweisungen hat, wird die Deduktion für jede return-Anweisung durchgeführt. Alle resultierenden Typen müssen identisch sein und werden zum tatsächlichen Rückgabetyp.
Wenn eine solche Funktion keine return-Anweisung hat, ist
Hinweis: Die Bedeutung des decltype ( auto ) -Platzhalters in Variablen- und Funktionsdeklarationen verwendet keine Template-Argument-Deduktion. |
(seit C++14) |
Überladenauflösung
Template-Argument-Deduktion wird während der
Überladungsauflösung
verwendet, wenn Spezialisierungen aus einer Kandidaten-Template-Funktion generiert werden.
P
und
A
sind dieselben wie in einem regulären Funktionsaufruf:
std::string s; std::getline(std::cin, s); // "std::getline" benennt 4 Funktions-Templates, // davon sind 2 Kandidaten-Funktionen (korrekte Parameteranzahl) // 1. Kandidaten-Template: // P1 = std::basic_istream<CharT, Traits>&, A1 = std::cin // P2 = std::basic_string<CharT, Traits, Allocator>&, A2 = s // Deduktion bestimmt die Typ-Template-Parameter CharT, Traits und Allocator // Spezialisierung std::getline<char, std::char_traits<char>, std::allocator<char>> // 2. Kandidaten-Template: // P1 = std::basic_istream<CharT, Traits>&&, A1 = std::cin // P2 = std::basic_string<CharT, Traits, Allocator>&, A2 = s // Deduktion bestimmt die Typ-Template-Parameter CharT, Traits und Allocator // Spezialisierung std::getline<char, std::char_traits<char>, std::allocator<char>> // Überladungsauflösung stuft die Referenzbindung vom Lvalue std::cin ein // und wählt die erste der beiden Kandidaten-Spezialisierungen
Wenn die Deduktion fehlschlägt oder wenn die Deduktion erfolgreich ist, aber die erzeugte Spezialisierung ungültig wäre (zum Beispiel ein überladener Operator, dessen Parameter weder Klassen- noch Enumerationstypen sind), wird die Spezialisierung nicht in den Überladungssatz aufgenommen, ähnlich wie bei SFINAE .
Adresse einer Überladungsmenge
Template Argument Deduction wird verwendet, wenn die Adresse eines überladenen Sets genommen wird, was Funktions-Templates einschließt.
Der Funktionstyp der Funktionsvorlage ist
P
. Der
Zieltyp
ist der Typ von
A
:
std::cout << std::endl; // std::endl bezeichnet eine Funktionsvorlage // Typ von endl P = // std::basic_ostream<CharT, Traits>& (std::basic_ostream<CharT, Traits>&) // operator<< Parameter A = // std::basic_ostream<char, std::char_traits<char>>& (*)( // std::basic_ostream<char, std::char_traits<char>>& // ) // (andere Überladungen von operator<< sind nicht geeignet) // Deduktion bestimmt die Typ-Template-Parameter CharT und Traits
Eine zusätzliche Regel wird in diesem Fall auf die Deduktion angewendet: Beim Vergleichen von Funktionsparametern
P
i und
A
i, wenn irgendein
P
i eine Rvalue-Referenz auf einen cv-unqualifizierten Template-Parameter ist (eine "Forwarding Reference") und das entsprechende
A
i eine Lvalue-Referenz ist, dann wird
P
i zum Template-Parametertyp angepasst (T&& wird zu T).
|
Wenn der Rückgabetyp der Funktionsvorlage ein Platzhalter ist ( auto oder decltype ( auto ) ), ist dieser Rückgabetyp ein nicht abgeleiteter Kontext und wird durch die Instanziierung bestimmt. |
(seit C++14) |
Partielle Ordnung
Template-Argument-Deduktion wird während der partiellen Ordnung überladener Funktions-Templates verwendet.
|
Dieser Abschnitt ist unvollständig
Grund: Mini-Beispiel |
Konvertierungsfunktionsvorlage
Template Argument Deduction wird verwendet, wenn user-defined conversion function Template-Argumente ausgewählt werden.
A
ist der Typ, der als Ergebnis der Konvertierung benötigt wird.
P
ist der Rückgabetyp der Konvertierungsfunktionsvorlage. Wenn
P
ein Referenztyp ist, dann wird der referenzierte Typ anstelle von
P
für die folgenden Teile dieses Abschnitts verwendet.
Wenn
A
kein Referenztyp ist:
P
ein Array-Typ ist, dann wird der durch Array-zu-Pointer-Konvertierung erhaltene Pointer-Typ anstelle von
P
verwendet;
P
ein Funktionstyp ist, dann wird der durch Funktion-zu-Zeiger-Konvertierung erhaltene Funktionszeigertyp anstelle von
P
verwendet;
P
cv-qualifiziert ist, werden die obersten CV-Qualifizierer ignoriert.
Wenn
A
cv-qualifiziert ist, werden die obersten CV-Qualifizierer ignoriert. Wenn
A
ein Referenztyp ist, wird der referenzierte Typ für die Deduktion verwendet.
Falls die übliche Deduktion von
P
und
A
(wie oben beschrieben) fehlschlägt, werden zusätzlich die folgenden Alternativen in Betracht gezogen:
A
ein Referenztyp ist,
A
kann stärker cv-qualifiziert sein als das abgeleitete
A
;
A
ein Zeiger- oder Zeiger-auf-Member-Typ ist, darf das abgeleitete
A
jeder Zeiger sein, der durch Qualifikationskonvertierung in
A
umgewandelt werden kann:
struct C { template<class T> operator T***(); }; C c; const int* const* const* p1 = c; // P = T***, A = const int* const* const* // reguläre Funktionsaufruf-Deduktion für // template<class T> void f(T*** p) als wäre sie mit dem Argument // vom Typ const int* const* const* aufgerufen, schlägt fehl // zusätzliche Deduktion für Konvertierungsfunktionen bestimmt T = int // (abgeleitetes A ist int***, konvertierbar zu const int* const* const*)
|
c)
wenn
A
ein Funktionszeigertyp ist, darf der abgeleitete
A
ein Zeiger auf eine noexcept-Funktion sein, der durch Funktionszeigerkonvertierung in
A
konvertierbar ist;
d)
wenn
A
ein Zeiger auf eine Elementfunktion ist, darf der abgeleitete
A
ein Zeiger auf eine noexcept-Elementfunktion sein, der durch Funktionszeigerkonvertierung in
A
konvertierbar ist.
|
(seit C++17) |
Siehe member template für weitere Regeln bezüglich Conversion Function Templates.
Explizite Instanziierung
Template Argument Deduction wird verwendet in expliziten Instanziierungen , expliziten Spezialisierungen und jenen Friend-Deklarationen , bei denen der Deklarator-ID zufällig auf eine Spezialisierung einer Funktions-Template verweist (zum Beispiel, friend ostream & operator << <> ( ... ) ), falls nicht alle Template-Argumente explizit angegeben oder vorgegeben sind, wird Template Argument Deduction verwendet, um zu bestimmen, auf welche Template-Spezialisierung verwiesen wird.
P
ist der Typ der Funktionsvorlage, die als potenzielle Übereinstimmung in Betracht gezogen wird, und
A
ist der Funktionstyp aus der Deklaration. Wenn es keine Übereinstimmungen oder mehr als eine Übereinstimmung (nach partieller Ordnung) gibt, ist die Funktionsdeklaration fehlerhaft:
template<class X> void f(X a); // 1. Template f template<class X> void f(X* a); // 2. Template f template<> void f<>(int* a) {} // Explizite Spezialisierung von f // P1 = void(X), A1 = void(int*): abgeleitet X = int*, f<int*>(int*) // P2 = void(X*), A2 = void(int*): abgeleitet X = int, f<int>(int*) // f<int*>(int*) und f<int>(int*) werden dann der partiellen Ordnung unterzogen // welche f<int>(int*) als das spezialisiertere Template auswählt
Eine zusätzliche Regel wird in diesem Fall auf die Deduktion angewendet: Beim Vergleichen von Funktionsparametern
P
i und
A
i, wenn irgendein
P
i eine Rvalue-Referenz auf einen cv-unqualifizierten Template-Parameter ist (eine "Forwarding Reference") und das entsprechende
A
i eine Lvalue-Referenz ist, dann wird
P
i zum Template-Parametertyp angepasst (T&& wird zu T).
Deallokationsfunktions-Template
Template-Argument-Deduktion wird verwendet, um zu bestimmen, ob eine
Deallocation-Funktion
-Template-Spezialisierung mit einer gegebenen Placement-Form von
operator new
übereinstimmt.
P
ist der Typ der Funktionsvorlage, die als potenzielle Übereinstimmung betrachtet wird, und
A
ist der Funktionstyp der Deallokationsfunktion, die die Übereinstimmung für den betrachteten Placement-Operator new wäre. Wenn keine Übereinstimmung oder mehr als eine Übereinstimmung vorliegt (nach Überladungsauflösung), wird die Placement-Deallokationsfunktion nicht aufgerufen (Speicherleck kann auftreten):
struct X { X() { throw std::runtime_error(""); } static void* operator new(std::size_t sz, bool b) { return ::operator new(sz); } static void* operator new(std::size_t sz, double f) { return ::operator new(sz); } template<typename T> static void operator delete(void* ptr, T arg) { ::operator delete(ptr); } }; int main() { try { X* p1 = new (true) X; // wenn X() wirft, wird operator delete gesucht // P1 = void(void*, T), A1 = void(void*, bool): // abgeleitetes T = bool // P2 = void(void*, T), A2 = void(void*, double): // abgeleitetes T = double // Überladungsauflösung wählt operator delete<bool> } catch(const std::exception&) {} try { X* p1 = new (13.2) X; // gleiche Suche, wählt operator delete<double> } catch(const std::exception&) {} }
Alias-Vorlagen
Alias-Templates werden nicht abgeleitet , außer bei Class Template Argument Deduction (seit C++20) :
template<class T> struct Alloc {}; template<class T> using Vec = vector<T, Alloc<T>>; Vec<int> v; template<template<class, class> class TT> void g(TT<int, Alloc<int>>); g(v); // OK: TT = vector abgeleitet template<template<class> class TT> void f(TT<int>); f(v); // Fehler: TT kann nicht als "Vec" abgeleitet werden, da Vec ein Alias-Template ist
Implizite Konvertierungen
Die Typableitung berücksichtigt keine impliziten Konvertierungen (außer den oben aufgeführten Typanpassungen): Das ist die Aufgabe der
Überlagerungsauflösung
, die später stattfindet. Wenn jedoch die Ableitung für alle Parameter, die an der Template-Argumentableitung teilnehmen, erfolgreich ist, und alle nicht abgeleiteten Template-Argumente explizit angegeben oder standardmäßig sind, dann werden die verbleibenden Funktionsparameter mit den entsprechenden Funktionsargumenten verglichen. Für jeden verbleibenden Parameter
P
mit einem Typ, der vor der Substitution von explizit angegebenen Template-Argumenten nicht abhängig war, schlägt die Ableitung fehl, wenn das entsprechende Argument
A
nicht implizit zu
P
konvertiert werden kann.
Parameter mit abhängigen Typen, an denen keine Template-Parameter an der Template-Argumentableitung teilnehmen, und Parameter, die durch Substitution explizit angegebener Template-Argumente nicht-abhängig geworden sind, werden während der Überladungsauflösung überprüft:
template<class T> struct Z { typedef typename T::x xx; }; template<class T> typename Z<T>::xx f(void*, T); // #1 template<class T> void f(int, T); // #2 struct A {} a; int main() { f(1, a); // für #1: Deduktion bestimmt T = struct A, aber das verbleibende Argument 1 // kann nicht implizit zu seinem Parameter void* konvertiert werden: Deduktion scheitert // Instanziierung des Rückgabetyps wird nicht angefordert // für #2: Deduktion bestimmt T = struct A, und das verbleibende Argument 1 // kann implizit zu seinem Parameter int konvertiert werden: Deduktion erfolgreich // der Funktionsaufruf kompiliert als Aufruf von #2 (Deduktionsfehler ist SFINAE) }
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 70 | C++98 | ob Arraygrenzen abgeleitet werden, war nicht spezifiziert | als nicht abgeleitet spezifiziert |
| CWG 300 | C++98 |
Ableitung fand statt für Funktionsparameter der Form
type(*)(T)/T(*)()/T(*)(T)
, Funktionszeiger
passen zu diesen Formen, Funktionsreferenzen jedoch nicht |
diese Formen geändert zu
type(T)/T()/T(T)
damit sie
auch Referenzen abdecken können |
| CWG 322 | C++98 |
Typparameter von Referenztypen wurden nicht
angepasst, um den referenzierten Typ für die Ableitung zu verwenden |
Anpassung hinzugefügt |
| CWG 976 | C++98 |
bei der Ableitung für Konvertierungsoperatoren-Templates
const T&
Rückgabetyp konnte niemals mit
T
Ergebnistyp übereinstimmen
|
Regeln angepasst um
solche Übereinstimmungen zu erlauben |
| CWG 1387 | C++11 | der Ausdruck eines decltype-Spezifizierers war kein nicht-abgeleiteter Kontext | ist er |
| CWG 1391 | C++98 |
Effekt von impliziten Konvertierungen der Argumente
die nicht an der Ableitung beteiligt sind, war nicht spezifiziert |
wie oben beschrieben spezifiziert |
| CWG 1591 | C++11 | kann Arraygrenze und Elementtyp nicht von einer braced-init-list ableiten | Ableitung erlaubt |
| CWG 2052 | C++98 |
Ableiten eines Operators mit Nicht-Klassen
Nicht-Enum-Argumenten war ein schwerer Fehler |
weicher Fehler wenn
andere Overloads vorhanden sind |
| CWG 2091 | C++98 |
Ableiten eines Referenz-Konstanten-Parameters funktionierte nicht
aufgrund von Typinkongruenz mit dem Argument |
Typinkongruenz vermieden |
| N3922 | C++11 | Direct-List-Initialisierung von auto leitet std::initializer_list ab |
ill-formed für mehr als ein
Element, leitet Elementtyp für einzelnes Element ab |
| CWG 2355 | C++17 | Wert in einem noexcept Spezifizierer eines Funktionstyps war nicht ableitbar | ableitbar gemacht |