Schablonen-Argument-Deduktion
Um eine Funktionstemplates zu instanziieren, müssen alle Template-Argumente bekannt sein, aber nicht alle Template-Argumente müssen angegeben werden. Wenn möglich, leitet der Compiler die fehlenden Template-Argumente aus den Funktionsargumenten ab. Dies geschieht, wenn ein Funktionsaufruf versucht wird, wenn eine Adresse eines Funktionstemplate genommen wird, und in einigen anderen Kontexten.
template<typename To, typename From> To convert(From f); void g(double d) { int i = convert<int>(d); // calls convert<int, double>(double) char c = convert<char>(d); // calls convert<char, double>(double) int(*ptr)(float) = convert; // instantiates convert<int, float>(float) // and stores its address 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 ihn als Ausdruck eines Funktionsaufrufs neu zu schreiben.
Die Template-Argument-Deduktion findet nach der Namenssuche für Funktionstemplates (die argumentabhängige Suche beinhalten kann) und vor der Template-Argument-Substitution (die SFINAE und Überladungsauflösung beinhalten kann) statt.
|
Die 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); Die Template-Argument-Deduktion für Klassentemplates findet in Deklarationen und in expliziten Cast-Ausdrücken statt; siehe Klassen-Template-Argument-Deduktion für Details. |
(seit C++17) |
Inhalt |
[bearbeiten] Deduktion aus einem Funktionsaufruf
Die Template-Argument-Deduktion versucht, Template-Argumente (Typen für Typ-Template-Parameter Ti, Templates für Template-Template-Parameter TTi und Werte für Nicht-Typ-Template-Parameter Ii) zu bestimmen, die in jeden Parameter P substituiert werden können, um den Typ deduziert A zu erzeugen, der dem Typ des Arguments A entspricht, nach den unten aufgeführten Anpassungen.
Wenn es mehrere Parameter gibt, wird jedes P/A-Paar separat deduziert und die deduzierten Template-Argumente werden dann kombiniert. Wenn die Deduktion für ein P/A-Paar fehlschlägt oder mehrdeutig ist, oder wenn verschiedene Paare unterschiedliche deduzierte Template-Argumente ergeben, oder wenn ein Template-Argument weder deduziert noch explizit angegeben bleibt, schlägt die Kompilierung fehl.
|
Wenn Referenzen und cv-Qualifizierer 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 Referenzen und cv-Qualifizierer 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 eine 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, ein Zeiger auf einen Funktionstyp oder ein Zeiger auf einen Member-Funktionstyp ist und A eine Menge überladener Funktionen ist, die keine Funktionstemplates enthält, wird die 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 deduzierbarer Kontext (siehe unten).
template<class T> int f(T(*p)(T)); int g(int); int g(char); f(g); // P = T(*)(T), A = overload set // P = T(*)(T), A1 = int(int): deduced T = int // P = T(*)(T), A2 = int(char): fails to deduce T // only one overload works, deduction succeeds
Bevor die Deduktion beginnt, werden die folgenden Anpassungen an P und A vorgenommen.
P kein Referenztyp ist,A ein Array-Typ ist, wird A durch den Zeigertyp ersetzt, der durch Array-zu-Zeiger-Konvertierung erhalten wird;A ein Funktionstyp ist, wird A durch den Zeigertyp ersetzt, der durch Funktions-zu-Zeiger-Konvertierung erhalten wird;A ein cv-qualifizierter Typ ist, werden die Top-Level-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 Top-Level-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 Weiterleitungsreferenzen) und das entsprechende Funktionsaufrufargument 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 Aktion von std::forward. Hinweis: In der Klassen-Template-Argument-Deduktion ist der Template-Parameter eines Klassentemplates niemals eine Weiterleitungsreferenz(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 erfolgt die Deduktion wie unten beschrieben (vgl. Abschnitt Deduktion aus 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) mit dem *transformierten* A, d. h. A nach den oben aufgeführten Anpassungen, identisch machen.
Wenn die übliche Deduktion von P und A fehlschlägt, werden zusätzlich folgende Alternativen in Betracht gezogen.
P ein Referenztyp ist, kann das deduzierte A (d. h. der von der Referenz referenzierte Typ) cv-qualifizierter 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 Member-Typ sein, der durch eine Qualifizierungskonvertierung oder eine Funktionszeigerkonvertierung(seit C++17) in das deduzierte 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 eines einfachen Template-IDs hat, kann das transformierte A eine abgeleitete Klasse des deduzierten A sein. Ebenso, wenn P ein Zeiger auf eine Klasse der Form einfache Template-ID ist, kann das transformierte A ein Zeiger auf eine abgeleitete Klasse sein, auf die vom deduzierten A gezeigt wird.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 }
[bearbeiten] Nicht deduzierbare Kontexte
In den folgenden Fällen nehmen die Typen, Templates und Nicht-Typ-Werte, die zur Zusammensetzung von P verwendet werden, nicht an der Template-Argument-Deduktion teil, sondern *verwenden* stattdessen die Template-Argumente, die entweder woanders deduziert oder explizit angegeben wurden. Wenn ein Template-Parameter nur in nicht deduzierbaren Kontexten verwendet und nicht explizit angegeben wird, schlägt die Template-Argument-Deduktion 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-Indexing-Spezifizierer oder ein Pack-Indexing-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, so dass mehr als eine Funktion zu P passt oder keine Funktion zu P passt oder die Menge der Überladungen ein oder mehrere Funktionstemplates enthält.P, dessen A eine geschweifte Initialisierungsliste ist, aber P kein std::initializer_list ist, eine Referenz darauf (möglicherweise cv-qualifiziert) oder eine Referenz auf ein Array ist.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 is in non-deduced context // error: T is not explicitly specified or deduced from another P/A g2({1, 2, 3}, 10); // P1 = std::vector<T>, A1 = {1, 2, 3}: T is in non-deduced context // P2 = T, A2 = int: deduced T = int
|
8) Der Parameter
P, der ein Parameter-Pack 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 Pack-Expansion 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 ein Teil eines Typnamens nicht deduzierbar ist, ist der gesamte Typname ein nicht deduzierbarer Kontext. Zusammengesetzte Typen können jedoch sowohl deduzierbare als auch nicht deduzierbare Typnamen enthalten. Zum Beispiel ist in A<T>::B<T2> T nicht deduzierbar wegen Regel #1 (verschachtelter Namensspezifizierer) und T2 ist nicht deduzierbar, weil es Teil desselben Typnamens ist. Aber in void(*f)(typename A<T>::B, A<T>) ist das T in A<T>::B nicht deduzierbar (wegen derselben Regel), während das T in A<T> deduziert wird.
[bearbeiten] Deduktion aus einem Typ
Gegeben einen Funktionsparameter P, der von einem oder mehreren Typ-Template-Parametern Ti, Template-Template-Parametern TTi oder Nicht-Typ-Template-Parametern Ii abhängt, und dem entsprechenden Argument A, findet die 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) stellt einen Typ oder eine Parameter-Typ-Liste dar, die entweder diese Regeln rekursiv erfüllt, ein nicht deduzierbarer Kontext inPoderAist oder derselbe nicht-abhängige Typ inPundAist. -
TT(optional) oderTU(optional) stellt entweder ein Klassentemplate oder ein Template-Template-Parameter dar. -
I(optional) stellt einen Ausdruck dar, der entweder einIist, inPoderAwertabhängig ist oder denselben konstanten Wert inPundAhat.
|
(seit C++17) |
Wenn P eine der Formen hat, die eine Template-Parameterliste <T> oder <I> enthalten, dann wird jedes Element Pi dieser Template-Argumentenliste mit dem entsprechenden Template-Argument Ai von seinem A abgeglichen. Wenn das letzte Pi eine Pack-Expansion ist, dann wird sein Muster mit jedem verbleibenden Argument in der Template-Argumentenliste von A verglichen. Ein nachgestellter Parameter-Pack, der nicht anderweitig deduziert wird, wird als leerer Parameter-Pack deduziert.
Wenn P eine der Formen hat, die eine Funktionsparameterliste (T) enthalten, dann wird jeder Parameter Pi aus dieser Liste mit dem entsprechenden Argument Ai aus der Funktionsparameterliste von A verglichen. Wenn das letzte Pi eine Pack-Expansion ist, dann wird sein Deklarator mit jedem verbleibenden Ai in der Parameter-Typ-Liste von A verglichen.
Formen können verschachtelt und rekursiv verarbeitet werden.
- X<int>(*)(char[6]) ist ein Beispiel für
T*, wobeiTX<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-Argumente können nicht aus dem Typ eines Nicht-Typ-Template-Arguments deduziert 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 can be deduced to equal 20 // but T cannot be deduced from the type of i |
(bis C++17) |
|
Wenn der Wert des Arguments, das einem Nicht-Typ-Template-Parameter P entspricht, der mit einem abhängigen Typ deklariert ist, aus einem Ausdruck deduziert wird, werden die Template-Parameter im Typ von P aus dem Typ des Wertes deduziert. 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 was deduced to long // from the template argument value in the type A<2> template<auto X> class bar {}; template<class T, T n> void f(bar<n> x); f(bar<3>{}); // OK: T was deduced to int (and n to 3) // from the template argument value in the type bar<3> Der Typ von template<class T, T i> void f(int (&a)[i]); int v[10]; f(v); // OK: T is 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 is deduced as true and the type of X is deduced as bool. |
(seit C++17) |
Wenn ein Nicht-Typ-Template-Parameter eines Funktionstemplate in der Template-Parameterliste eines Funktionparameters (der ebenfalls ein Template ist) verwendet wird und das entsprechende Template-Argument deduziert wird, muss der Typ des deduzierten Template-Arguments (wie in seiner umschließenden Template-Parameterliste angegeben, d. h. Referenzen werden beibehalten) exakt dem Typ des Nicht-Typ-Template-Parameters entsprechen, außer dass cv-Qualifizierer weggelassen werden, und außer wenn das Template-Argument aus einer Array-Grenze deduziert wird – in diesem Fall ist jeder integrale Typ zulässig, sogar bool, obwohl dieser immer zu true würde.
template<int i> class A {}; template<short s> void f(A<s>); // the type of the non-type template param is short void k1() { A<1> a; // the type of the non-type template param of a is int f(a); // P = A<(short)s>, A = A<(int)1> // error: deduced non-type template argument does not have the same // type as its corresponding template argument f<1>(a); // OK: the template argument is not deduced, // this calls f<(short)1>(A<(short)1>) } 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> // parameter type is int& // argument type is int& in struct X's template declaration // OK (with CWG 2091): deduces R to refer to n }
Typ-Template-Parameter können nicht aus dem Typ eines Standard-Funktionsarguments deduziert werden.
template<typename T> void f(T = 5, T = 7); void g() { f(1); // OK: calls f<int>(1, 7) f(); // error: cannot deduce T f<int>(); // OK: calls f<int>(5, 7) }
Die Deduktion 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 is a template with a TT param 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>: deduced TT = B, calls f(A<B>)
[bearbeiten] Andere Kontexte
Neben Funktionsaufrufen und Operator-Ausdrücken wird die Template-Argument-Deduktion in den folgenden Situationen verwendet.
auto-Typ-DeduktionDie Template-Argument-Deduktion wird in Deklarationen von Variablen verwendet, wenn die Bedeutung des auto-Spezifizierers aus dem Initialisierer der Variablen deduziert wird. Der Parameter const auto& x = 1 + 2; // P = const U&, A = 1 + 2: // same rules as for calling f(1 + 2) where f is // template<class U> void f(const U& u) // deduced U = int, the type of x is const int& auto l = {13}; // P = std::initializer_list<U>, A = {13}: // deduced U = int, the type of l is std::initializer_list<int> Bei direkter Listeninitialisierung (aber nicht bei Kopier-Listen-Initialisierung), wenn die Bedeutung von auto aus einer geschweiften Initialisierungsliste deduziert wird, muss die geschweifte Initialisierungsliste nur ein Element enthalten, und der Typ von auto ist der Typ dieses Elements. auto x1 = {3}; // x1 is std::initializer_list<int> auto x2{1, 2}; // error: not a single element auto x3{3}; // x3 is int // (before N3922 x2 and x3 were both std::initializer_list<int>) |
(seit C++11) |
auto-zurückgebende FunktionenDie Template-Argument-Deduktion wird in Deklarationen von Funktionen verwendet, wenn die Bedeutung des auto-Spezifizierers im Rückgabetyp der Funktion aus der return-Anweisung deduziert wird. Bei auto-zurückgebenden 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 gleich sein und werden zum tatsächlichen Rückgabetyp. Wenn eine solche Funktion keine return-Anweisung hat, ist Hinweis: Die Bedeutung des Platzhalters decltype(auto) in Variablen- und Funktionsdeklarationen verwendet keine Template-Argument-Deduktion. |
(seit C++14) |
[bearbeiten] Überladungsauflösung
Die 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 bei einem regulären Funktionsaufruf.
std::string s; std::getline(std::cin, s); // "std::getline" names 4 function templates, // 2 of which are candidate functions (correct number of parameters) // 1st candidate template: // P1 = std::basic_istream<CharT, Traits>&, A1 = std::cin // P2 = std::basic_string<CharT, Traits, Allocator>&, A2 = s // deduction determines the type template parameters CharT, Traits, and Allocator // specialization std::getline<char, std::char_traits<char>, std::allocator<char>> // 2nd candidate template: // P1 = std::basic_istream<CharT, Traits>&&, A1 = std::cin // P2 = std::basic_string<CharT, Traits, Allocator>&, A2 = s // deduction determines the type template parameters CharT, Traits, and Allocator // specialization std::getline<char, std::char_traits<char>, std::allocator<char>> // overload resolution ranks reference binding from lvalue std::cin // and picks the first of the two candidate specializations
Wenn die Deduktion fehlschlägt oder wenn die Deduktion erfolgreich ist, aber die Spezialisierung, die sie erzeugt, ungültig wäre (z. B. ein überladener Operator, dessen Parameter weder Klassen- noch Enumerationstypen sind), wird die Spezialisierung nicht in die Überladungsmenge aufgenommen, ähnlich wie bei SFINAE.
[bearbeiten] Adresse einer Überladungsmenge
Die Template-Argument-Deduktion wird beim Ermitteln einer Adresse einer Überladungsmenge verwendet, die Funktionstemplates enthält.
Der Funktionstyp des Funktionstemplate ist P. Der Zieltyp ist der Typ von A.
std::cout << std::endl; // std::endl names a function template // type of 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>>& // ) // (other overloads of operator<< are not viable) // deduction determines the type template parameters CharT and Traits
Eine zusätzliche Regel wird auf die Deduktion in diesem Fall angewendet: Beim Vergleichen von Funktionsparametern Pi und Ai, wenn ein Pi eine rvalue-Referenz auf einen cv-unqualifizierten Template-Parameter ist (eine "Weiterleitungsreferenz") und das entsprechende Ai eine lvalue-Referenz ist, dann wird Pi auf den Template-Parametertyp (T&& wird zu T) angepasst.
|
Wenn der Rückgabetyp des Funktionstemplate ein Platzhalter ist (auto oder decltype(auto)), ist dieser Rückgabetyp ein nicht deduzierbarer Kontext und wird aus der Instanziierung ermittelt. |
(seit C++14) |
[bearbeiten] Partielle Ordnung
Die Template-Argument-Deduktion wird während der partiellen Ordnung von überladenen Funktionstemplates verwendet.
| Dieser Abschnitt ist unvollständig Grund: Mini-Beispiel |
[bearbeiten] Konvertierungsfunktions-Template
Die Template-Argument-Deduktion wird bei der Auswahl von benutzerdefinierten Konvertierungsfunktions-Template-Argumenten verwendet.
A ist der Typ, der als Ergebnis der Konvertierung benötigt wird. P ist der Rückgabetyp des Konvertierungsfunktions-Templates. Wenn P ein Referenztyp ist, dann wird der referenzierte Typ anstelle von P für die folgenden Teile des Abschnitts verwendet.
Wenn A kein Referenztyp ist
P ein Array-Typ ist, dann wird der Zeigertyp, der durch Array-zu-Zeiger-Konvertierung erhalten wird, anstelle von P verwendet;P ein Funktionstyp ist, dann wird der Funktionzeigertyp, der durch Funktions-zu-Zeiger-Konvertierung erhalten wird, anstelle von P verwendet;P cv-qualifiziert ist, werden die Top-Level-cv-Qualifizierer ignoriert.Wenn A cv-qualifiziert ist, werden die Top-Level-cv-Qualifizierer ignoriert. Wenn A ein Referenztyp ist, wird der referenzierte Typ von der Deduktion verwendet.
Wenn die übliche Deduktion von P und A (wie oben beschrieben) fehlschlägt, werden zusätzlich folgende Alternativen in Betracht gezogen.
A ein Referenztyp ist, kann A cv-qualifizierter sein als das deduzierte A;A ein Zeiger oder Zeiger auf Member-Typ ist, darf das deduzierte A jeder Zeiger sein, der durch Qualifizierungskonvertierung in A konvertiert werden kann.struct C { template<class T> operator T***(); }; C c; const int* const* const* p1 = c; // P = T***, A = const int* const* const* // regular function-call deduction for // template<class T> void f(T*** p) as if called with the argument // of type const int* const* const* fails // additional deduction for conversion functions determines T = int // (deduced A is int***, convertible to const int* const* const*)
|
c) Wenn
A ein Funktionzeigertyp ist, darf das deduzierte A ein Zeiger auf eine noexcept-Funktion sein, die durch Funktionszeigerkonvertierung in A konvertierbar ist;d) Wenn
A ein Zeiger auf eine Member-Funktion ist, darf das deduzierte A ein Zeiger auf eine noexcept-Member-Funktion sein, der durch Funktionszeigerkonvertierung in A konvertierbar ist. |
(seit C++17) |
Siehe Member-Templates für weitere Regeln bezüglich Konvertierungsfunktions-Templates.
[bearbeiten] Explizite Instanziierung
Die Template-Argument-Deduktion wird in expliziten Instanziierungen, expliziten Spezialisierungen und jenen friend-Deklarationen verwendet, bei denen der Deklarator-ID zufällig auf eine Spezialisierung eines Funktionstemplate verweist (z. B. friend ostream& operator<< <> (...)), wenn nicht alle Template-Argumente explizit angegeben oder standardmäßig gesetzt sind, wird die Template-Argument-Deduktion verwendet, um zu bestimmen, welche Template-Spezialisierung referenziert wird.
P ist der Typ des Funktionstemplate, das als potenzieller Treffer betrachtet wird, und A ist der Funktionstyp aus der Deklaration. Wenn es keine Übereinstimmungen oder mehr als eine Übereinstimmung gibt (nach partieller Ordnung), ist die Funktionsdeklaration fehlerhaft.
template<class X> void f(X a); // 1st template f template<class X> void f(X* a); // 2nd template f template<> void f<>(int* a) {} // explicit specialization of f // P1 = void(X), A1 = void(int*): deduced X = int*, f<int*>(int*) // P2 = void(X*), A2 = void(int*): deduced X = int, f<int>(int*) // f<int*>(int*) and f<int>(int*) are then submitted to partial ordering // which selects f<int>(int*) as the more specialized template
Eine zusätzliche Regel wird auf die Deduktion in diesem Fall angewendet: Beim Vergleichen von Funktionsparametern Pi und Ai, wenn ein Pi eine rvalue-Referenz auf einen cv-unqualifizierten Template-Parameter ist (eine "Weiterleitungsreferenz") und das entsprechende Ai eine lvalue-Referenz ist, dann wird Pi auf den Template-Parametertyp (T&& wird zu T) angepasst.
[bearbeiten] Deallokationsfunktionstemplates
Die Template-Argument-Deduktion wird verwendet, um zu bestimmen, ob eine Deallokationsfunktion-Template-Spezialisierung zu einer gegebenen Platzierungsform von operator new passt.
P ist der Typ des Funktionstemplate, das als potenzieller Treffer betrachtet wird, und A ist der Funktionstyp der Deallokationsfunktion, die der Platzierungsoperator new unter Berücksichtigung entsprechen würde. Wenn es keine Übereinstimmung oder mehr als eine Übereinstimmung gibt (nach Überladungsauflösung), wird die Platzierungs-Deallokationsfunktion nicht aufgerufen (Speicherverlust 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; // when X() throws, operator delete is looked up // P1 = void(void*, T), A1 = void(void*, bool): // deduced T = bool // P2 = void(void*, T), A2 = void(void*, double): // deduced T = double // overload resolution picks operator delete<bool> } catch(const std::exception&) {} try { X* p1 = new (13.2) X; // same lookup, picks operator delete<double> } catch(const std::exception&) {} }
[bearbeiten] Alias-Templates
Alias-Templates werden nicht deduziert, außer bei der Klassen-Template-Argument-Deduktion(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: deduced TT = vector template<template<class> class TT> void f(TT<int>); f(v); // error: TT cannot be deduced as "Vec" because Vec is an alias template
[bearbeiten] Implizite Konvertierungen
Die Typableitung berücksichtigt keine impliziten Konvertierungen (außer den oben genannten Typanpassungen): Dies ist die Aufgabe der Überladungsauflösung, die später stattfindet. Wenn jedoch die Ableitung für alle Parameter, die an der Template-Argumentableitung teilnehmen, erfolgreich ist und alle Template-Argumente, die nicht abgeleitet werden, explizit angegeben oder standardmäßig gesetzt 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 in P konvertiert werden kann.
Parameter mit abhängigen Typen, bei denen keine Template-Parameter an der Template-Argumentableitung teilnehmen, und Parameter, die aufgrund der Substitution von explizit angegebenen Template-Argumenten nicht mehr abhängig 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); // for #1, deduction determines T = struct A, but the remaining argument 1 // cannot be implicitly converted to its parameter void*: deduction fails // instantiation of the return type is not requested // for #2, deduction determines T = struct A, and the remaining argument 1 // can be implicitly converted to its parameter int: deduction succeeds // the function call compiles as a call to #2 (deduction failure is SFINAE) }
[bearbeiten] Defect reports
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 70 | C++98 | ob Array-Grenzen abgeleitet werden würden, war nicht spezifiziert | als nicht ableitbar spezifiziert |
| CWG 300 | C++98 | die Ableitung fand für Funktionsparameter der Form statttype(*)(T)/T(*)()/T(*)(T), Funktionszeigerstimmen mit diesen Formen überein, Funktionsreferenzen jedoch nicht |
diese Formen zutype(T)/T()/T(T) geändert, damit sieauch Referenzen abdecken können |
| CWG 322 | C++98 | Typ-Parameter 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 Konvertierungsoperator-Templates konnte der Rückgabetyp const T& nie mit dem Ergebnistyp T übereinstimmen |
Regeln angepasst, um solche Übereinstimmungen zu ermöglichen |
| CWG 1387 | C++11 | der Ausdruck eines decltype-Spezifizierers war kein nicht ableitbarer Kontext | sie ist |
| CWG 1391 | C++98 | die Auswirkung von impliziten Konvertierungen der Argumente die nicht an der Ableitung beteiligt sind, war nicht spezifiziert |
wie oben beschrieben spezifiziert |
| CWG 1591 | C++11 | Array-Grenze und Elementtyp können nicht aus einer initializer list abgeleitet werden | Ableitung erlaubt |
| CWG 2052 | C++98 | die Ableitung eines Operators mit Nicht-Klassen Nicht-Enum-Argumenten war ein harter Fehler |
weicher Fehler, wenn es andere Überladungen gibt |
| CWG 2091 | C++98 | die Ableitung eines Referenz-Nicht-Typ-Parameters funktionierte nicht aufgrund eines Typkonflikts mit dem Argument |
Typkonflikt vermieden |
| N3922 | C++11 | Direkt-List-Initialisierung von auto leitet std::initializer_list ab | fehlerhaft für mehr als ein Element, leitet Elementtyp für ein einzelnes Element ab |
| CWG 2355 | C++17 | der Wert in einem noexcept-Spezifizierer eines Funktionstyps war nicht ableitbar | ableitbar gemacht |