Member-Templates
Template-Deklarationen (Klassen, Funktions und Variablen(seit C++14)) können innerhalb einer Mitgliedsspezifikation einer beliebigen Klasse, Struktur oder Union erscheinen, die keine lokalen Klassen sind.
#include <algorithm> #include <iostream> #include <string> #include <vector> struct Printer { // generic functor std::ostream& os; Printer(std::ostream& os) : os(os) {} template<typename T> void operator()(const T& obj) { os << obj << ' '; } // member template }; int main() { std::vector<int> v{1,2,3}; std::for_each(v.begin(), v.end(), Printer(std::cout)); std::string s{"abc"}; std::ranges::for_each(s, Printer(std::cout)); }
Ausgabe
1 2 3 a b c
Partielle Spezialisierungen von Member-Templates können sowohl im Klassenscope als auch im umschließenden Namespace-Scope erscheinen. Explizite Spezialisierungen können in jedem Scope erscheinen, in dem das primäre Template erscheinen kann.
struct A { template<class T> struct B; // primary member template template<class T> struct B<T*> {}; // OK: partial specialization // template<> struct B<int*> {}; // OK via CWG 727: full specialization }; template<> struct A::B<int*> {}; // OK template<class T> struct A::B<T&> {}; // OK
Wenn die umschließende Klassendeklaration wiederum ein Klassentemplate ist, muss ein Member-Template, wenn es außerhalb des Klassenkörpers definiert wird, zwei Sätze von Template-Parametern verwenden: einen für die umschließende Klasse und einen für sich selbst.
template<typename T1> struct string { // member template function template<typename T2> int compare(const T2&); // constructors can be templates too template<typename T2> string(const std::basic_string<T2>& s) { /*...*/ } }; // out of class definition of string<T1>::compare<T2> template<typename T1> // for the enclosing class template template<typename T2> // for the member template int string<T1>::compare(const T2& s) { /* ... */ }
Inhalt |
[bearbeiten] Member-Funktions-Templates
Destruktoren und Kopierkonstruktoren können keine Templates sein. Wenn ein Template-Konstruktor deklariert wird, der mit der Typsignatur eines Kopierkonstruktors instanziiert werden könnte, wird stattdessen der implizit deklarierte Kopierkonstruktor verwendet.
Ein Member-Funktions-Template kann nicht virtuell sein, und ein Member-Funktions-Template in einer abgeleiteten Klasse kann keine virtuelle Member-Funktion aus der Basisklasse überschreiben.
class Base { virtual void f(int); }; struct Derived : Base { // this member template does not override Base::f template<class T> void f(T); // non-template member override can call the template: void f(int i) override { f<>(i); } };
Eine Nicht-Template-Member-Funktion und eine Template-Member-Funktion mit demselben Namen können deklariert werden. Im Konfliktfall (wenn eine Template-Spezialisierung exakt mit der Nicht-Template-Funktionssignatur übereinstimmt) bezieht sich die Verwendung dieses Namens und Typs auf die Nicht-Template-Mitgliedsfunktion, es sei denn, eine explizite Template-Argumentliste wird angegeben.
template<typename T> struct A { void f(int); // non-template member template<typename T2> void f(T2); // member template }; // template member definition template<typename T> template<typename T2> void A<T>::f(T2) { // some code } int main() { A<char> ac; ac.f('c'); // calls template function A<char>::f<char>(char) ac.f(1); // calls non-template function A<char>::f(int) ac.f<>(1); // calls template function A<char>::f<int>(int) }
Eine Out-of-Class-Definition eines Member-Funktions-Templates muss äquivalent zur Deklaration innerhalb der Klasse sein (siehe Funktions-Template-Überladung für die Definition von Äquivalenz), andernfalls wird sie als Überladung betrachtet.
struct X { template<class T> T good(T n); template<class T> T bad(T n); }; template<class T> struct identity { using type = T; }; // OK: equivalent declaration template<class V> V X::good(V n) { return n; } // Error: not equivalent to any of the declarations inside X template<class T> T X::bad(typename identity<T>::type n) { return n; }
[bearbeiten] Konvertierungsfunktions-Templates
Eine benutzerdefinierte Konvertierungsfunktion kann ein Template sein.
struct A { template<typename T> operator T*(); // conversion to pointer to any type }; // out-of-class definition template<typename T> A::operator T*() { return nullptr; } // explicit specialization for char* template<> A::operator char*() { return nullptr; } // explicit instantiation template A::operator void*(); int main() { A a; int* ip = a.operator int*(); // explicit call to A::operator int*() }
Während der Überladungsauflösung werden Spezialisierungen von Konvertierungsfunktions-Templates nicht durch Namenssuche gefunden. Stattdessen werden alle sichtbaren Konvertierungsfunktions-Templates berücksichtigt, und jede durch Template-Argument-Deduktion (die spezielle Regeln für Konvertierungsfunktions-Templates hat) erzeugte Spezialisierung wird so verwendet, als wäre sie durch Namenssuche gefunden worden.
Using-Deklarationen in abgeleiteten Klassen können sich nicht auf Spezialisierungen von Template-Konvertierungsfunktionen aus Basisklassen beziehen.
|
Ein benutzerdefiniertes Konvertierungsfunktions-Template kann keinen abgeleiteten Rückgabetyp haben. struct S { operator auto() const { return 10; } // OK template<class T> operator auto() const { return 42; } // error }; |
(seit C++14) |
Member-Variablen-TemplatesEine Variablen-Template-Deklaration kann im Klassenscope erscheinen, in diesem Fall deklariert sie ein statisches Datenmember-Template. Details finden Sie unter Variablen-Templates. |
(seit C++14) |
[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 1878 | C++14 | operator auto war technisch erlaubt | operator auto verboten |