Namensräume
Varianten
Aktionen

Funktionsschablone

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
Klassenschablone
Funktionsschablone
Sonstiges
 
 
 
 

Eine Funktionsvorlage definiert eine Familie von Funktionen.

Inhalt

[bearbeiten] Syntax

template < parameter-liste > funktionsdeklaration (1)
template < parameter-liste > requires bedingung funktionsdeklaration (2) (seit C++20)
funktionsdeklaration-mit-platzhaltern (3) (seit C++20)
export template < parameter-liste > funktionsdeklaration (4) (in C++11 entfernt)

[bearbeiten] Erklärung

parameter-liste - eine nicht-leere, durch Kommas getrennte Liste der Vorlagenparameter, von denen jeder entweder ein Nicht-Typ-Parameter, ein Typ-Parameter, ein Vorlagen-Parameter oder ein Parameter-Pack von einem dieser ist(seit C++11). Wie bei jeder Vorlage können Parameter eingeschränkt sein(seit C++20)
funktionsdeklaration - eine Funktionsdeklaration. Der deklarierte Funktionsname wird zu einem Vorlagennamen.
constraint - ein Bedingungsausdruck, der die von dieser Funktionsvorlage akzeptierten Vorlagenparameter einschränkt
funktionsdeklaration-
mit-platzhaltern
- eine Funktionsdeklaration, bei der der Typ mindestens eines Parameters den Platzhalter auto oder Concept auto verwendet: Die Vorlagenparameterliste erhält einen erfundenen Parameter für jeden Platzhalter (siehe Abgekürzte Funktionsvorlagen unten)

export war ein optionaler Modifikator, der die Vorlage als exportiert deklarierte (wenn er mit einer Klassenvorlage verwendet wurde, deklarierte er auch alle ihre Member als exportiert). Dateien, die exportierte Vorlagen instanziierten, mussten ihre Definitionen nicht einschließen: die Deklaration war ausreichend. Implementierungen von export waren selten und stimmten in Details nicht überein.

(bis C++11)

Abgekürzte Funktionstemplates

Wenn Platzhaltertypen (entweder auto oder Concept auto) in der Parameterliste einer Funktionsdeklaration oder einer Funktionsvorlagendeklaration erscheinen, deklariert die Deklaration eine Funktionsvorlage, und ein erfundener Vorlagenparameter für jeden Platzhalter wird an die Vorlagenparameterliste angehängt.

void f1(auto); // same as template<class T> void f1(T)
void f2(C1 auto); // same as template<C1 T> void f2(T), if C1 is a concept
void f3(C2 auto...); // same as template<C2... Ts> void f3(Ts...), if C2 is a concept
void f4(const C3 auto*, C4 auto&); // same as template<C3 T, C4 U> void f4(const T*, U&);
 
template<class T, C U>
void g(T x, U y, C auto z); // same as template<class T, C U, C W> void g(T x, U y, W z);

Abgekürzte Funktionsvorlagen können wie alle Funktionsvorlagen spezialisiert werden.

template<>
void f4<int>(const int*, const double&); // specialization of f4<int, const double>


(seit C++20)

[bearbeiten] Signatur einer Funktionsvorlage

Jede Funktionsvorlage hat eine Signatur.

Die Signatur eines template-head ist die Vorlagenparameterliste, ohne die Namen der Vorlagenparameter und Standardargumente, und die requires-Klausel (falls vorhanden)(seit C++20).

Die Signatur einer Funktionsvorlage enthält den Namen, die Parameter-Typ-Liste, den Rückgabetyp, die nachfolgende requires-Klausel (falls vorhanden)(seit C++20) und die Signatur des template-head. Mit Ausnahme der folgenden Fälle enthält ihre Signatur auch den umschließenden Namensraum.

Wenn die Funktionsvorlage ein Klassen-Member ist, enthält ihre Signatur die Klasse, deren Member die Funktion ist, anstelle des umschließenden Namensraums. Ihre Signatur enthält auch die nachfolgende requires-Klausel (falls vorhanden)(seit C++20), den ref-Qualifizierer (falls vorhanden) und(seit C++11) die cv-Qualifizierer (falls vorhanden).

Wenn die Funktionsvorlage eine friend-Funktion mit einer Bedingung ist, die umschließende Vorlagenparameter betrifft, enthält ihre Signatur die umschließende Klasse anstelle des umschließenden Namensraums.

(seit C++20)

[bearbeiten] Instanziierung einer Funktionsvorlage

Eine Funktionsvorlage ist für sich allein kein Typ und keine Funktion. Aus einer Quelldatei, die nur Vorlagendefinitionen enthält, wird kein Code generiert. Damit überhaupt Code erscheint, muss eine Vorlage instanziiert werden: die Vorlagenargumente müssen bestimmt werden, damit der Compiler eine tatsächliche Funktion (oder Klasse, aus einer Klassenvorlage) generieren kann.

[bearbeiten] Explizite Instanziierung

template rückgabetyp name < argumentliste > ( parameterliste ) ; (1)
template rückgabetyp name ( parameterliste ) ; (2)
extern template rückgabetyp name < argumentliste > ( parameterliste ) ; (3) (seit C++11)
extern template rückgabetyp name ( parameterliste ) ; (4) (seit C++11)
1) Definition der expliziten Instanziierung (ohne Ableitung von Vorlagenargumenten, wenn jeder Nicht-Standard-Vorlagenparameter explizit angegeben ist)
2) Definition der expliziten Instanziierung mit Ableitung von Vorlagenargumenten für alle Parameter
3) Deklaration der expliziten Instanziierung (ohne Ableitung von Vorlagenargumenten, wenn jeder Nicht-Standard-Vorlagenparameter explizit angegeben ist)
4) Deklaration der expliziten Instanziierung mit Ableitung von Vorlagenargumenten für alle Parameter

Eine explizite Instanziierungsdefinition erzwingt die Instanziierung der Funktion oder der Memberfunktion, auf die sie sich bezieht. Sie kann im Programm überall nach der Vorlagendefinition erscheinen und darf für eine gegebene Argumentliste nur einmal im Programm vorkommen, ohne dass eine Diagnose erforderlich ist.

Eine explizite Instanziierungsdeklaration (eine externe Vorlage) verhindert implizite Instanziierungen: Der Code, der andernfalls eine implizite Instanziierung verursachen würde, muss die explizite Instanziierungsdefinition verwenden, die irgendwo anders im Programm bereitgestellt wird.

(seit C++11)

Ein nachfolgendes Vorlagenargument kann in einer expliziten Instanziierung einer Spezialisierung einer Funktionsvorlage oder einer Memberfunktionsvorlage weggelassen werden, wenn es aus dem Funktionsparameter abgeleitet werden kann.

template<typename T>
void f(T s)
{
    std::cout << s << '\n';
}
 
template void f<double>(double); // instantiates f<double>(double)
template void f<>(char);         // instantiates f<char>(char), template argument deduced
template void f(int);            // instantiates f<int>(int), template argument deduced

Die explizite Instanziierung einer Funktionsvorlage oder einer Memberfunktion einer Klassenvorlage kann inline oder constexpr nicht verwenden. Wenn die Deklaration der expliziten Instanziierung eine implizit deklarierte spezielle Memberfunktion benennt, ist das Programm fehlerhaft.

Die explizite Instanziierung eines Konstruktors kann keine Vorlagenparameterliste (Syntax (1)) verwenden, was auch nie notwendig ist, da sie abgeleitet werden können (Syntax (2)).

Die explizite Instanziierung eines prospektiven Destruktors muss den ausgewählten Destruktor der Klasse benennen.

(seit C++20)

Explizite Instanziierungsdeklarationen unterdrücken nicht die implizite Instanziierung von inline-Funktionen, auto-Deklarationen, Referenzen und Klassenvorlagenspezialisierungen. (Somit wird, wenn die Inline-Funktion, die Gegenstand der expliziten Instanziierungsdeklaration ist, ODR-verwendet wird, sie für Inlining implizit instanziiert, aber ihre Out-of-line-Kopie wird in dieser Translationseinheit nicht generiert.)

Die explizite Instanziierungsdefinition einer Funktionsvorlage mit Standardargumenten ist keine Verwendung der Argumente und versucht nicht, sie zu initialisieren.

char* p = 0;
 
template<class T>
T g(T x = &p) { return x; }
 
template int g<int>(int); // OK even though &p isn’t an int.

[bearbeiten] Implizite Instanziierung

Wenn Code auf eine Funktion in einem Kontext zugreift, der die Funktionsdefinition erfordert, oder wenn die Existenz der Definition die Semantik des Programms beeinflusst(seit C++11), und diese spezielle Funktion nicht explizit instanziiert wurde, tritt eine implizite Instanziierung auf. Die Liste der Vorlagenargumente muss nicht angegeben werden, wenn sie aus dem Kontext abgeleitet werden kann.

#include <iostream>
 
template<typename T>
void f(T s)
{
    std::cout << s << '\n';
}
 
int main()
{
    f<double>(1); // instantiates and calls f<double>(double)
    f<>('a');     // instantiates and calls f<char>(char)
    f(7);         // instantiates and calls f<int>(int)
    void (*pf)(std::string) = f; // instantiates f<string>(string)
    pf("∇");                     // calls f<string>(string)
}

Die Existenz einer Funktionsdefinition gilt als Einflussnahme auf die Semantik des Programms, wenn die Funktion für die Konstantenbewertung durch einen Ausdruck benötigt wird, selbst wenn die Konstantenbewertung des Ausdrucks nicht erforderlich ist oder wenn die Konstanten-Ausdrucksbewertung die Definition nicht verwendet.

template<typename T>
constexpr int f() { return T::value; }
 
template<bool B, typename T>
void g(decltype(B ? f<T>() : 0));
template<bool B, typename T>
void g(...);
 
template<bool B, typename T>
void h(decltype(int{B ? f<T>() : 0}));
template<bool B, typename T>
void h(...);
 
void x()
{
    g<false, int>(0); // OK: B ? f<T>() : 0 is not potentially constant evaluated
    h<false, int>(0); // error: instantiates f<int> even though B evaluates to false
                      // and list-initialization of int from int cannot be narrowing
}
(seit C++11)

Hinweis: Das vollständige Weglassen von <> erlaubt der Überladungsauflösung, sowohl Vorlagen- als auch Nicht-Vorlagenüberladungen zu prüfen.

[bearbeiten] Ableitung von Vorlagenargumenten

Um eine Funktionsvorlage zu instanziieren, muss jedes Vorlagenargument bekannt sein, aber nicht jedes Vorlagenargument muss angegeben werden. Wenn möglich, leitet der Compiler die fehlenden Vorlagenargumente aus den Funktionsargumenten ab. Dies geschieht, wenn ein Funktionsaufruf versucht wird und wenn die Adresse einer Funktionsvorlage genommen wird.

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)
}

Dieser Mechanismus macht es möglich, Vorlagenoperatoren zu verwenden, da es keine Syntax gibt, um Vorlagenargumente für einen Operator anzugeben, außer durch Umschreiben als Funktionsaufrufausdruck.

#include <iostream>
 
int main() 
{
    std::cout << "Hello, world" << std::endl;
    // operator<< is looked up via ADL as std::operator<<,
    // then deduced to operator<<<char, std::char_traits<char>> both times
    // std::endl is deduced to &std::endl<char, std::char_traits<char>>
}

Die Ableitung von Vorlagenargumenten erfolgt nach der Namenssuche für die Funktionsvorlage (die eine argumentabhängige Suche beinhalten kann) und vor der Überladungsauflösung.

Siehe Ableitung von Vorlagenargumenten für Details.

[bearbeiten] Explizite Vorlagenargumente

Vorlagenargumente einer Funktionsvorlage können erhalten werden aus

  • Ableitung von Vorlagenargumenten
  • Standard-Vorlagenargumente
  • explizit angegeben, was in den folgenden Kontexten geschehen kann
  • in einem Funktionsaufrufausdruck
  • wenn eine Adresse einer Funktion genommen wird
  • wenn eine Referenz auf eine Funktion initialisiert wird
  • wenn ein Zeiger auf eine Memberfunktion gebildet wird
  • in einer expliziten Spezialisierung
  • in einer expliziten Instanziierung
  • in einer friend-Deklaration

Es gibt keine Möglichkeit, Vorlagenargumente für überladene Operatoren, Konvertierungsfunktionen und Konstruktoren explizit anzugeben, da diese ohne Verwendung des Funktionsnamens aufgerufen werden.

Die angegebenen Vorlagenargumente müssen den Vorlagenparametern in ihrer Art entsprechen (d. h. Typ für Typ, Nicht-Typ für Nicht-Typ und Vorlage für Vorlage). Es dürfen nicht mehr Argumente als Parameter vorhanden sein (es sei denn, ein Parameter ist ein Parameter-Pack, in diesem Fall muss für jeden Nicht-Pack-Parameter ein Argument vorhanden sein)(seit C++11).

Die angegebenen Nicht-Typ-Argumente müssen entweder den Typen der entsprechenden Nicht-Typ-Vorlagenparameter entsprechen oder in sie konvertierbar sein.

Die Funktionsparameter, die nicht an der Ableitung von Vorlagenargumenten teilnehmen (z. B. wenn die entsprechenden Vorlagenargumente explizit angegeben werden), unterliegen impliziten Konvertierungen in den Typ des entsprechenden Funktionsparameters (wie bei der üblichen Überladungsauflösung).

Ein Vorlagenparameter-Pack, das explizit angegeben wird, kann durch Ableitung von Vorlagenargumenten erweitert werden, wenn zusätzliche Argumente vorhanden sind.

template<class... Types>
void f(Types... values);
 
void g()
{
    f<int*, float*>(0, 0, 0); // Types = {int*, float*, int}
}
(seit C++11)

[bearbeiten] Substitution von Vorlagenargumenten

Wenn alle Vorlagenargumente angegeben, abgeleitet oder aus Standard-Vorlagenargumenten erhalten wurden, wird jede Verwendung eines Vorlagenparameters in der Funktionsparameterliste durch die entsprechenden Vorlagenargumente ersetzt.

Substitutionsfehler (d. h. Fehler beim Ersetzen von Vorlagenparametern durch die abgeleiteten oder bereitgestellten Vorlagenargumente) einer Funktionsvorlage entfernt die Funktionsvorlage aus der Überladungsmenge. Dies ermöglicht eine Reihe von Möglichkeiten zur Manipulation von Überladungsmengen mittels Vorlagenmetaprogrammierung: siehe SFINAE für Details.

Nach der Substitution werden alle Funktionsparameter vom Array- und Funktionstyp zu Zeigern und alle Top-Level-cv-Qualifizierer von Funktionsparametern fallen gelassen (wie bei einer regulären Funktionsdeklaration).

Das Entfernen der Top-Level-cv-Qualifizierer beeinflusst nicht den Typ des Parameters, wie er innerhalb der Funktion erscheint.

template<class T>
void f(T t);
 
template<class X>
void g(const X x);
 
template<class Z>
void h(Z z, Z* zp);
 
// two different functions with the same type, but 
// within the function, t has different cv qualifications
f<int>(1);       // function type is void(int), t is int
f<const int>(1); // function type is void(int), t is const int
 
// two different functions with the same type and the same x
// (pointers to these two functions are not equal,
//  and function-local statics would have different addresses)
g<int>(1);       // function type is void(int), x is const int
g<const int>(1); // function type is void(int), x is const int
 
// only top-level cv-qualifiers are dropped:
h<const int>(1, NULL); // function type is void(int, const int*) 
                       // z is const int, zp is const int*

[bearbeiten] Überladung von Funktionsvorlagen

Funktionsvorlagen und Nicht-Vorlagenfunktionen können überladen werden.

Eine Nicht-Vorlagenfunktion ist immer von einer Vorlagenspezialisierung mit demselben Typ verschieden. Spezialisierungen verschiedener Funktionsvorlagen sind immer voneinander verschieden, auch wenn sie denselben Typ haben. Zwei Funktionsvorlagen mit demselben Rückgabetyp und derselben Parameterliste sind unterschiedlich und können durch ihre explizite Vorlagenargumentliste unterschieden werden.

Wenn ein Ausdruck, der Typ- oder Nicht-Typ-Vorlagenparameter verwendet, in der Funktionsparameterliste oder im Rückgabetyp erscheint, bleibt dieser Ausdruck für die Überladung Teil der Signatur der Funktionsvorlage.

template<int I, int J>
A<I+J> f(A<I>, A<J>); // overload #1
 
template<int K, int L>
A<K+L> f(A<K>, A<L>); // same as #1
 
template<int I, int J>
A<I-J> f(A<I>, A<J>); // overload #2

Zwei Ausdrücke, die Vorlagenparameter beinhalten, werden als äquivalent bezeichnet, wenn zwei Funktionsdefinitionen, die diese Ausdrücke enthalten, unter ODR gleich wären, d. h., die beiden Ausdrücke enthalten dieselbe Abfolge von Token, deren Namen durch Namenssuche denselben Entitäten zugeordnet werden, außer dass Vorlagenparameter unterschiedlich benannt sein können. Zwei Lambda-Ausdrücke sind niemals äquivalent.(seit C++20)

template<int I, int J>
void f(A<I+J>); // template overload #1
 
template<int K, int L>
void f(A<K+L>); // equivalent to #1

Bei der Bestimmung, ob zwei abhängige Ausdrücke äquivalent sind, werden nur die beteiligten abhängigen Namen betrachtet, nicht die Ergebnisse der Namenssuche. Wenn mehrere Deklarationen derselben Vorlage sich im Ergebnis der Namenssuche unterscheiden, wird die erste solche Deklaration verwendet.

template<class T>
decltype(g(T())) h(); // decltype(g(T())) is a dependent type
 
int g(int);
 
template<class T>
decltype(g(T())) h()
{                  // redeclaration of h() uses earlier lookup
    return g(T()); // although the lookup here does find g(int)
}
 
int i = h<int>(); // template argument substitution fails; g(int)
                  // was not in scope at the first declaration of h()

Zwei Funktionsvorlagen gelten als äquivalent, wenn

  • sie im selben Gültigkeitsbereich deklariert sind
  • sie denselben Namen haben
  • sie *äquivalente* Vorlagenparameterlisten haben, d. h. die Listen sind gleich lang, und für jedes entsprechende Parameterpaar gilt Folgendes:
  • die beiden Parameter sind von derselben Art (beide Typen, beide Nicht-Typen oder beide Vorlagen)
  • sie sind entweder beide Parameter-Packs oder keines
(seit C++11)
  • wenn Nicht-Typ, ihre Typen sind äquivalent;
  • wenn Vorlage, ihre Vorlagenparameter sind äquivalent;
  • wenn einer mit Konzeptname deklariert ist, sind beide es, und die Konzeptnamen sind äquivalent.
(seit C++20)
  • die Ausdrücke, die Vorlagenparameter in ihren Rückgabetypen und Parameterlisten beinhalten, *äquivalent* sind
  • die Ausdrücke in ihren requires-Klauseln, die auf die Vorlagenparameterlisten folgen, falls vorhanden, äquivalent sind
  • die Ausdrücke in ihren requires-Klauseln, die den Funktionsdeklaratoren folgen, falls vorhanden, äquivalent sind
(seit C++20)

Zwei potenziell ausgewertete(seit C++20) Ausdrücke, die Vorlagenparameter beinhalten, werden als *funktional äquivalent* bezeichnet, wenn sie nicht *äquivalent* sind, aber für eine beliebige gegebene Menge von Vorlagenargumenten die Auswertung der beiden Ausdrücke zum selben Wert führt.

Zwei Funktionsvorlagen gelten als *funktional äquivalent*, wenn sie *äquivalent* sind, abgesehen davon, dass ein oder mehrere Ausdrücke, die Vorlagenparameter in ihren Rückgabetypen und Parameterlisten beinhalten, *funktional äquivalent* sind.

Darüber hinaus sind zwei Funktionsvorlagen *funktional äquivalent*, aber nicht *äquivalent*, wenn ihre Bedingungen unterschiedlich spezifiziert sind, sie aber die gleichen Listen von Vorlagenargumenten akzeptieren und erfüllen.

(seit C++20)

Wenn ein Programm Deklarationen von Funktionsvorlagen enthält, die *funktional äquivalent*, aber nicht *äquivalent* sind, ist das Programm fehlerhaft; keine Diagnose erforderlich.

// equivalent
template<int I>
void f(A<I>, A<I+10>); // overload #1
template<int I>
void f(A<I>, A<I+10>); // redeclaration of overload #1
 
// not equivalent
template<int I>
void f(A<I>, A<I+10>); // overload #1
template<int I>
void f(A<I>, A<I+11>); // overload #2
 
// functionally-equivalent but not equivalent
// This program is ill-formed, no diagnostic required
template<int I>
void f(A<I>, A<I+10>);      // overload #1
template<int I>
void f(A<I>, A<I+1+2+3+4>); // functionally equivalent

Wenn dieselbe Spezialisierung einer Funktionsvorlage mehr als eine überladene Funktionsvorlage trifft (was oft aus der Ableitung von Vorlagenargumenten resultiert), wird eine *partielle Ordnung von überladenen Funktionsvorlagen* durchgeführt, um die beste Übereinstimmung auszuwählen.

Insbesondere findet eine partielle Ordnung in den folgenden Situationen statt:

1) Überladungsauflösung für einen Aufruf einer Spezialisierung einer Funktionsvorlage
template<class X>
void f(X a);
template<class X>
void f(X* a);
 
int* p;
f(p);
2) wenn die Adresse einer Spezialisierung einer Funktionsvorlage genommen wird
template<class X>
void f(X a);
template<class X>
void f(X* a);
 
void (*p)(int*) = &f;
3) wenn ein Placement Operator Delete, der eine Spezialisierung einer Funktionsvorlage ist, ausgewählt wird, um einem Placement Operator New zu entsprechen
4) wenn eine Friend-Funktionsdeklaration, eine explizite Instanziierung oder eine explizite Spezialisierung auf eine Spezialisierung einer Funktionsvorlage verweist
template<class X>
void f(X a);  // first template f
template<class X>
void f(X* a); // second template f
template<>
void f<>(int *a) {} // explicit specialization
 
// template argument deduction comes up with two candidates:
// f<int*>(int*) and f<int>(int*)
// partial ordering selects f<int>(int*) as more specialized

Informell bedeutet "A ist spezialisierter als B", dass "A weniger Typen akzeptiert als B".

Formal betrachtet, um zu bestimmen, welche von zwei Funktionsvorlagen spezialisierter ist, transformiert der partielle Ordnungsprozess zunächst eine der beiden Vorlagen wie folgt:

  • Für jeden Typ-, Nicht-Typ- und Vorlagenparameter, einschließlich Parameter-Packs,(seit C++11) wird ein eindeutiger fiktiver Typ, Wert oder eine Vorlage generiert und in den Funktionstyp der Vorlage substituiert.
  • Wenn nur eine der beiden verglichenen Funktionsvorlagen eine Memberfunktion ist und diese Funktionsvorlage eine Nicht-Static-Member einer Klasse A ist, wird ihrer Parameterliste ein neuer erster Parameter hinzugefügt. Gegeben sei cv als die cv-Qualifizierer der Funktionsvorlage und ref als der ref-Qualifizierer der Funktionsvorlage(seit C++11), ist der neue Parametertyp cv A&, es sei denn, ref ist && oder ref ist nicht vorhanden und der erste Parameter der anderen Vorlage hat den Typ einer Rvalue-Referenz. In diesem Fall ist der Typ cv A&&(seit C++11). Dies hilft bei der Ordnung von Operatoren, die sowohl als Member- als auch als Nicht-Member-Funktionen gesucht werden.
struct A {};
 
template<class T>
struct B
{
    template<class R>
    int operator*(R&); // #1
};
 
template<class T, class R>
int operator*(T&, R&); // #2
 
int main()
{
    A a;
    B<A> b;
    b * a; // template argument deduction for int B<A>::operator*(R&) gives R=A 
           //                             for int operator*(T&, R&), T=B<A>, R=A
 
    // For the purpose of partial ordering, the member template B<A>::operator*
    // is transformed into template<class R> int operator*(B<A>&, R&);
 
    // partial ordering between 
    //     int operator*(   T&, R&)  T=B<A>, R=A
    // and int operator*(B<A>&, R&)  R=A 
    // selects int operator*(B<A>&, A&) as more specialized
}

Nachdem eine der beiden Vorlagen wie oben beschrieben transformiert wurde, wird die Ableitung von Vorlagenargumenten durchgeführt, wobei die transformierte Vorlage als Argumentvorlage und der ursprüngliche Vorlagentyp der anderen Vorlage als Parameter-Vorlage verwendet wird. Der Prozess wird dann wiederholt, wobei die zweite Vorlage (nach Transformationen) als Argument und die erste Vorlage in ihrer ursprünglichen Form als Parameter verwendet wird.

Die zur Bestimmung der Ordnung verwendeten Typen hängen vom Kontext ab:

  • im Kontext eines Funktionsaufrufs sind die Typen die Funktionsparametertypen, für die der Funktionsaufruf Argumente hat (Standard-Funktionsargumente, Parameter-Packs,(seit C++11) und Ellipsenparameter werden nicht berücksichtigt - siehe Beispiele unten)
  • im Kontext eines Aufrufs einer benutzerdefinierten Konvertierungsfunktion werden die Rückgabetypen der Konvertierungsfunktionsvorlagen verwendet.
  • in anderen Kontexten wird der Typ der Funktionsvorlage verwendet.

Jeder Typ aus der obigen Liste der Parameter-Vorlage wird abgeleitet. Bevor die Ableitung beginnt, wird jeder Parameter P der Parameter-Vorlage und das entsprechende Argument A der Argument-Vorlage wie folgt angepasst:

  • Wenn sowohl P als auch A vor dem Aufruf Referenztypen waren, wird bestimmt, welcher von beiden stärker cv-qualifiziert ist (in allen anderen Fällen werden cv-Qualifizierungen für die partielle Ordnung ignoriert).
  • Wenn P ein Referenztyp ist, wird er durch den referenzierten Typ ersetzt.
  • Wenn A ein Referenztyp ist, wird er durch den referenzierten Typ ersetzt.
  • Wenn P cv-qualifiziert ist, wird P durch die cv-unqualifizierte Version davon ersetzt.
  • Wenn A cv-qualifiziert ist, wird A durch die cv-unqualifizierte Version davon ersetzt.

Nach diesen Anpassungen erfolgt die Ableitung von P aus A gemäß Ableitung von Vorlagenargumenten aus einem Typ.

Wenn P ein Funktionsparameter-Pack ist, wird der Typ A jedes verbleibenden Parametertyps der Argument-Vorlage mit dem Typ P des Deklarator-IDs des Funktionsparameter-Packs verglichen. Jeder Vergleich leitet Vorlagenargumente für nachfolgende Positionen in den durch das Funktionsparameter-Pack expandierten Vorlagenparameter-Packs ab.

Wenn A aus einem Funktionsparameter-Pack transformiert wurde, wird es mit jedem verbleibenden Parametertyp der Parameter-Vorlage verglichen.

(seit C++11)

Wenn das Argument A der transformierten Vorlage-1 verwendet werden kann, um den entsprechenden Parameter P der Vorlage-2 abzuleiten, aber nicht umgekehrt, dann ist dieses A spezialisierter als P in Bezug auf den/die durch dieses P/A-Paar abgeleiteten Typ(en).

Wenn die Ableitung in beiden Richtungen erfolgreich ist und die ursprünglichen P und A Referenztypen waren, werden zusätzliche Tests durchgeführt:

  • Wenn A eine Lvalue-Referenz war und P eine Rvalue-Referenz, wird A als spezialisierter als P betrachtet.
  • Wenn A stärker cv-qualifiziert war als P, wird A als spezialisierter als P betrachtet.

In allen anderen Fällen ist keine Vorlage spezialisierter als die andere in Bezug auf den/die durch dieses P/A-Paar abgeleiteten Typ(en).

Nachdem jeder P und A in beiden Richtungen betrachtet wurde, gilt Folgendes, wenn für jeden betrachteten Typ gilt:

  • Vorlage-1 ist für alle Typen mindestens so spezialisiert wie Vorlage-2
  • Vorlage-1 ist für einige Typen spezialisierter als Vorlage-2
  • Vorlage-2 ist für keinen Typ spezialisierter als Vorlage-1 ODER ist für keinen Typ mindestens so spezialisiert

Dann ist Vorlage-1 spezialisierter als Vorlage-2. Wenn die obigen Bedingungen nach Vertauschen der Vorlagenreihenfolge zutreffen, dann ist Vorlage-2 spezialisierter als Vorlage-1. Andernfalls ist keine Vorlage spezialisierter als die andere.

Bei einem Gleichstand, wenn eine Funktionsvorlage einen nachfolgenden Parameter-Pack hat und die andere nicht, gilt die mit dem weggelassenen Parameter als spezialisierter als die mit dem leeren Parameter-Pack.

(seit C++11)

Wenn nach der Betrachtung aller Paare von überladenen Vorlagen eine eindeutig spezialisierter ist als alle anderen, wird die Spezialisierung dieser Vorlage ausgewählt, andernfalls schlägt die Kompilierung fehl.

In den folgenden Beispielen werden die fiktiven Argumente U1, U2 genannt.

template<class T>
void f(T);        // template #1
template<class T>
void f(T*);       // template #2
template<class T>
void f(const T*); // template #3
 
void m()
{
    const int* p;
    f(p); // overload resolution picks: #1: void f(T ) [T = const int *]
          //                            #2: void f(T*) [T = const int]
          //                            #3: void f(const T *) [T = int]
 
    // partial ordering:
 
    // #1 from transformed #2: void(T) from void(U1*): P=T A=U1*: deduction ok: T=U1*
    // #2 from transformed #1: void(T*) from void(U1): P=T* A=U1: deduction fails
    // #2 is more specialized than #1 with regards to T
 
    // #1 from transformed #3: void(T) from void(const U1*): P=T, A=const U1*: ok
    // #3 from transformed #1: void(const T*) from void(U1): P=const T*, A=U1: fails
    // #3 is more specialized than #1 with regards to T
 
    // #2 from transformed #3: void(T*) from void(const U1*): P=T* A=const U1*: ok
    // #3 from transformed #2: void(const T*) from void(U1*): P=const T* A=U1*: fails
    // #3 is more specialized than #2 with regards to T
 
    // result: #3 is selected
    // in other words, f(const T*) is more specialized than f(T) or f(T*)
}
template<class T>
void f(T, T*);   // #1
template<class T>
void f(T, int*); // #2
 
void m(int* p)
{
    f(0, p); // deduction for #1: void f(T, T*) [T = int]
             // deduction for #2: void f(T, int*) [T = int]
 
    // partial ordering:
 
    // #1 from #2: void(T,T*) from void(U1,int*): P1=T, A1=U1: T=U1
    //                                            P2=T*, A2=int*: T=int: fails
 
    // #2 from #1: void(T,int*) from void(U1,U2*): P1=T A1=U1: T=U1
    //                                             P2=int* A2=U2*: fails
 
    // neither is more specialized w.r.t T, the call is ambiguous
}
template<class T>
void g(T);  // template #1
template<class T>
void g(T&); // template #2
 
void m()
{
    float x;
    g(x); // deduction from #1: void g(T ) [T = float]
          // deduction from #2: void g(T&) [T = float]
 
    // partial ordering:
 
    // #1 from #2: void(T) from void(U1&): P=T, A=U1 (after adjustment), ok
 
    // #2 from #1: void(T&) from void(U1): P=T (after adjustment), A=U1: ok
 
    // neither is more specialized w.r.t T, the call is ambiguous
}
template<class T>
struct A { A(); };
 
template<class T>
void h(const T&); // #1
template<class T>
void h(A<T>&);    // #2
 
void m()
{
    A<int> z;
    h(z); // deduction from #1: void h(const T &) [T = A<int>]
          // deduction from #2: void h(A<T> &) [T = int]
 
    // partial ordering:
 
    // #1 from #2: void(const T&) from void(A<U1>&): P=T A=A<U1>: ok T=A<U1>
 
    // #2 from #1: void(A<T>&) from void(const U1&): P=A<T> A=const U1: fails
 
    // #2 is more specialized than #1 w.r.t T
 
    const A<int> z2;
    h(z2); // deduction from #1: void h(const T&) [T = A<int>]
           // deduction from #2: void h(A<T>&) [T = int], but substitution fails
 
    // only one overload to choose from, partial ordering not tried, #1 is called
}

Da ein Aufrufkontext nur Parameter berücksichtigt, für die es explizite Aufrufargumente gibt, werden diese Funktionsparameter-Packs,(seit C++11) Ellipsenparameter und Parameter mit Standardargumenten, für die kein explizites Aufrufargument vorhanden ist, ignoriert.

template<class T>
void f(T);         // #1
template<class T>
void f(T*, int = 1); // #2
 
void m(int* ip)
{
    int* ip;
    f(ip); // calls #2 (T* is more specialized than T)
}
template<class T>
void g(T);       // #1
template<class T>
void g(T*, ...); // #2
 
void m(int* ip)
{
    g(ip); // calls #2 (T* is more specialized than T)
}
template<class T, class U>
struct A {};
 
template<class T, class U>
void f(U, A<U, T>* p = 0); // #1
template<class U>
void f(U, A<U, U>* p = 0); // #2
 
void h()
{
    f<int>(42, (A<int, int>*)0); // calls #2
    f<int>(42);                  // error: ambiguous
}
template<class T>
void g(T, T = T()); // #1
template<class T, class... U>
void g(T, U...);    // #2
 
void h()
{
    g(42); // error: ambiguous
}
template<class T, class... U>
void f(T, U...); // #1
template<class T>
void f(T);       // #2
 
void h(int i)
{
    f(&i); // calls #2 due to the tie-breaker between parameter pack and no parameter
           // (note: was ambiguous between DR692 and DR1395)
}
template<class T, class... U>
void g(T*, U...); // #1
template<class T>
void g(T);        // #2
 
void h(int i)
{
    g(&i); // OK: calls #1 (T* is more specialized than T)
}
template<class... T>
int f(T*...);    // #1
template<class T>
int f(const T&); // #2
 
f((int*)0); // OK: selects #2; non-variadic template is more specialized than
            // variadic template (was ambiguous before DR1395 because deduction
            // failed in both directions)
template<class... Args>
void f(Args... args);        // #1
template<class T1, class... Args>
void f(T1 a1, Args... args); // #2
template<class T1, class T2>
void f(T1 a1, T2 a2);        // #3
 
f();        // calls #1
f(1, 2, 3); // calls #2
f(1, 2);    // calls #3; non-variadic template #3 is more
            // specialized than the variadic templates #1 and #2

Bei der Ableitung von Vorlagenargumenten innerhalb des Prozesses der partiellen Ordnung müssen Vorlagenparameter nicht mit Argumenten übereinstimmen, wenn das Argument in keinem der für die partielle Ordnung betrachteten Typen verwendet wird.

template<class T>
T f(int); // #1
template<class T, class U>
T f(U);   // #2
 
void g()
{
    f<int>(1); // specialization of #1 is explicit: T f(int) [T = int]
               // specialization of #2 is deduced:  T f(U) [T = int, U = int]
 
    // partial ordering (only considering the argument type):
 
    // #1 from #2: T(int) from U1(U2): fails
    // #2 from #1: T(U) from U1(int): ok: U=int, T unused
 
    // calls #1
}

Die partielle Ordnung von Funktionsvorlagen, die Vorlagenparameter-Packs enthalten, ist unabhängig von der Anzahl der für diese Vorlagenparameter-Packs abgeleiteten Argumente.

template<class...>
struct Tuple {};
 
template<class... Types>
void g(Tuple<Types...>);      // #1
template<class T1, class... Types>
void g(Tuple<T1, Types...>);  // #2
template<class T1, class... Types>
void g(Tuple<T1, Types&...>); // #3
 
g(Tuple<>());            // calls #1
g(Tuple<int, float>());  // calls #2
g(Tuple<int, float&>()); // calls #3
g(Tuple<int>());         // calls #3
(seit C++11)

Um einen Aufruf einer Funktionsvorlage zu kompilieren, muss der Compiler zwischen Nicht-Vorlagen-Überladungen, Vorlagen-Überladungen und den Spezialisierungen der Vorlagen-Überladungen entscheiden.

template<class T>
void f(T);      // #1: template overload
template<class T>
void f(T*);     // #2: template overload
 
void f(double); // #3: non-template overload
template<>
void f(int);    // #4: specialization of #1
 
f('a');        // calls #1
f(new int(1)); // calls #2
f(1.0);        // calls #3
f(1);          // calls #4

[bearbeiten] Überladungen von Funktionen vs. Spezialisierungen von Funktionen

Beachten Sie, dass nur Nicht-Vorlagen- und Primärvorlagen-Überladungen an der Überladungsauflösung teilnehmen. Die Spezialisierungen sind keine Überladungen und werden nicht berücksichtigt. Erst nachdem die Überladungsauflösung die am besten passende primäre Funktionsvorlage ausgewählt hat, werden ihre Spezialisierungen geprüft, um zu sehen, ob eine besser passt.

template<class T>
void f(T);    // #1: overload for all types
template<>
void f(int*); // #2: specialization of #1 for pointers to int
template<class T>
void f(T*);   // #3: overload for all pointer types
 
f(new int(1)); // calls #3, even though specialization of #1 would be a perfect match

Es ist wichtig, diese Regel beim Ordnen der Header-Dateien einer Translationseinheit zu beachten. Für weitere Beispiele des Zusammenspiels zwischen Funktionsüberladungen und Funktionsspezialisierungen, unten erweitern.

Beispiele

Betrachten wir zunächst einige Szenarien, in denen die argumentabhängige Suche nicht verwendet wird. Dazu verwenden wir den Aufruf (f)(t). Wie in ADL beschrieben, unterdrückt das Einpacken des Funktionsnamens in Klammern die argumentabhängige Suche.

  • Mehrere Überladungen von f(), die vor dem Punkt der Referenzierung (POR) in g() deklariert sind.
#include <iostream>
 
struct A {};
 
template<class T>
void f(T)  { std::cout << "#1\n"; } // overload #1 before f() POR
template<class T>
void f(T*) { std::cout << "#2\n"; } // overload #2 before f() POR
 
template<class T>
void g(T* t) 
{
    (f)(t); // f() POR
}
 
int main()
{
    A* p = nullptr;
    g(p); // POR of g() and f()
}
 
// Both #1 and #2 are added to the candidate list;
// #2 is selected because it is a better match.

Ausgabe

#2


  • Eine besser passende Vorlagenüberladung wird nach POR deklariert.
#include <iostream>
 
struct A {};
 
template<class T>
void f(T)  { std::cout << "#1\n"; } // #1
 
template<class T>
void g(T* t) 
{
    (f)(t); // f() POR
}
 
template<class T>
void f(T*) { std::cout << "#2\n"; } // #2
 
int main()
{
    A* p = nullptr;
    g(p); // POR of g() and f()
}
 
// Only #1 is added to the candidate list; #2 is defined after POR;
// therefore, it is not considered for overloading even if it is a better match.

Ausgabe

#1


  • Eine besser passende explizite Vorlagenspezialisierung wird nach POR deklariert.
#include <iostream>
 
struct A {};
 
template<class T>
void f(T)    { std::cout << "#1\n"; } // #1
 
template<class T>
void g(T* t) 
{
    (f)(t); // f() POR
}
template<>
void f<>(A*) { std::cout << "#3\n"; } // #3
 
int main()
{
    A* p = nullptr;
    g(p); // POR of g() and f()
}
 
// #1 is added to the candidate list; #3 is a better match defined after POR. The
// candidate list consists of #1 which is eventually selected. After that, the explicit 
// specialization #3 of #1 declared after POI is selected because it is a better match. 
// This behavior is governed by 14.7.3/6 [temp.expl.spec] and has nothing to do with ADL.

Ausgabe

#3


  • Eine besser passende Vorlagenüberladung wird nach POR deklariert. Die am besten passende explizite Vorlagenspezialisierung wird nach der besser passenden Überladung deklariert.
#include <iostream>
 
struct A {};
 
template<class T>
void f(T)    { std::cout << "#1\n"; } // #1
 
template<class T>
void g(T* t) 
{
    (f)(t); // f() POR
}
 
template<class T>
void f(T*)   { std::cout << "#2\n"; } // #2
template<>
void f<>(A*) { std::cout << "#3\n"; } // #3
 
int main()
{
    A* p = nullptr;
    g(p); // POR of g() and f()
}
 
// #1 is the only member of the candidate list and it is eventually selected. 
// After that, the explicit specialization #3 is skipped because it actually 
// specializes #2 declared after POR.

Ausgabe

#1


Betrachten wir nun die Fälle, die die argumentabhängige Suche verwenden (d. h. wir verwenden das üblichere Aufrufformat f(t)).

  • Eine besser passende Vorlagenüberladung wird nach POR deklariert.
#include <iostream>
 
struct A {};
 
template<class T>
void f(T)  { std::cout << "#1\n"; } // #1
 
template<class T>
void g(T* t) 
{
    f(t); // f() POR
}
 
template<class T>
void f(T*) { std::cout << "#2\n"; } // #2
 
int main()
{
    A* p = nullptr;
    g(p); // POR of g() and f()
}
 
// #1 is added to the candidate list as a result of the ordinary lookup;
// #2 is defined after POR but it is added to the candidate list via ADL lookup.
// #2 is selected being the better match.

Ausgabe

#2


  • Eine besser passende Vorlagenüberladung wird nach POR deklariert. Die am besten passende explizite Vorlagenspezialisierung wird vor der besser passenden Überladung deklariert.
#include <iostream>
 
struct A {};
 
template<class T>
void f(T)    { std::cout << "#1\n"; } // #1
 
template<class T>
void g(T* t) 
{
    f(t); // f() POR
}
 
template<>
void f<>(A*) { std::cout << "#3\n"; } // #3
template<class T>
void f(T*)   { std::cout << "#2\n"; } // #2
 
int main()
{
    A* p = nullptr;
    g(p); // POR of g() and f()
}
 
// #1 is added to the candidate list as a result of the ordinary lookup;
// #2 is defined after POR but it is added to the candidate list via ADL lookup.
// #2 is selected among the primary templates, being the better match.
// Since #3 is declared before #2, it is an explicit specialization of #1.
// Hence the final selection is #2.

Ausgabe

#2


  • Eine besser passende Vorlagenüberladung wird nach POR deklariert. Die am besten passende explizite Vorlagenspezialisierung wird zuletzt deklariert.
#include <iostream>
 
struct A {};
 
template<class T>
void f(T)    { std::cout << "#1\n"; } // #1
 
template<class T>
void g(T* t) 
{
    f(t); // f() POR
}
 
template<class T>
void f(T*)   { std::cout << "#2\n"; } // #2
template<>
void f<>(A*) { std::cout << "#3\n"; } // #3
 
int main()
{
    A* p = nullptr;
    g(p); // POR of g() and f()
}
 
// #1 is added to the candidate list as a result of the ordinary lookup;
// #2 is defined after POR but it is added to the candidate list via ADL lookup.
// #2 is selected among the primary templates, being the better match.
// Since #3 is declared after #2, it is an explicit specialization of #2;
// therefore, selected as the function to call.

Ausgabe

#3


Immer wenn die Argumente einfache C++-Basistypen sind, gibt es keine ADL-zugehörigen Namensräume. Daher sind diese Szenarien identisch mit den obigen Nicht-ADL-Beispielen.

Für detaillierte Regeln zur Überladungsauflösung siehe Überladungsauflösung.

[bearbeiten] Spezialisierung von Funktionsvorlagen

[bearbeiten] Schlüsselwörter

template, extern (seit C++11)

[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 214 C++98 das genaue Verfahren der partiellen Ordnung war nicht spezifiziert Spezifikation hinzugefügt
CWG 532 C++98 die Reihenfolge zwischen einer Nicht-Static-Member-Funktionsvorlage
und einer Nicht-Member-Funktionsvorlage war nicht spezifiziert
Spezifikation hinzugefügt
CWG 581 C++98 die Vorlagenargumentliste in einer expliziten Spezialisierung oder
Instanziierung einer Konstruktorvorlage war erlaubt
verbieten
CWG 1321 C++98 war unklar, ob gleiche abhängige Namen in der
erste Deklaration und eine Neu-Deklaration sind äquivalent
sie sind äquivalent und
die Bedeutung ist dieselbe wie
in der ersten Deklaration
CWG 1395 C++11 Deduktion fehlgeschlagen, wenn A aus einem Pack kam,
und es gab keinen leeren Pack-Tiebreaker
Deduktion erlaubt,
Tiebreaker hinzugefügt
CWG 1406 C++11 der Typ des neuen ersten Parameters, der für
eine nicht-statische Member-Funktion-Template hinzugefügt wurde, war
nicht relevant für den Ref-Qualifier dieses Templates
der Typ ist ein Rvalue
Referenztyp, wenn der
Ref-Qualifier && ist
CWG 1446 C++11 der Typ des neuen ersten Parameters, der für eine nicht-statische Member-Funktion
hinzugefügt wurde, ohne Ref-Qualifier war ein Lvalue-Referenztyp
Typ, selbst wenn diese Member-Funktion-Template mit einer
Funktion-Template verglichen wird, deren erster Parameter den Rvalue-Referenztyp hat
der Typ ist ein
Rvalue-Referenz
Typ in diesem Fall
CWG 2373 C++98 neue erste Parameter wurden zu den Parameterlisten
von statischen Member-Funktion-Templates in der partiellen Ordnung hinzugefügt
nicht hinzugefügt

[bearbeiten] Siehe auch