Templates sind ein Werkzeug von C++, um parametrischen Polymorphismus zu unterstützen. Mithilfe von Templates können Benutzer ein allgemeines Muster für Klassen oder Funktionen deklarieren, sodass bestimmte Datenelemente in einer Klasse oder die Parameter und Rückgabewerte von Memberfunktionen beliebige Typen annehmen können.

Ein Template ist ein Werkzeug, das Typen parametrisiert;

Es gibt üblicherweise zwei Formen: Funktions-Templates und Klassen-Templates;

Funktions-Templates zielen auf Funktionen ab, die sich nur in den Parametertypen unterscheiden;

Klassen-Templates zielen auf Klassen ab, die sich nur in den Typen der Datenelemente und Memberfunktionen unterscheiden.

Der Zweck der Verwendung von Templates besteht darin, Programmierern zu ermöglichen, typunabhängigen Code zu schreiben. Wenn Sie beispielsweise eine swap-Funktion schreiben, die zwei int-Variablen vertauscht, kann diese Funktion nur int verarbeiten. Sie kann keine Typen wie double oder char verarbeiten. Um diese Typen zu vertauschen, müssten Sie eine weitere swap-Funktion schreiben. Der Zweck der Verwendung von Templates besteht darin, die Programmimplementierung typunabhängig zu machen. Beispielsweise kann eine swap-Template-Funktion sowohl int- als auch double-Vertauschungen verarbeiten. Templates können auf Funktionen und Klassen angewendet werden. Sie werden im Folgenden getrennt vorgestellt.

Hinweis:Die Deklaration oder Definition eines Templates kann nur im globalen Bereich, im Namespace-Bereich oder im Klassenbereich erfolgen. Das heißt, sie kann nicht im lokalen Bereich oder innerhalb einer Funktion erfolgen; zum Beispiel können Sie ein Template nicht in der main-Funktion deklarieren oder definieren.


I. Allgemeine Form von Funktions-Templates

1. Format eines Funktions-Templates:

template <class 形参名,class 形参名,......> 返回类型 函数名(参数列表)
{
    函数体
}

Dabei sind template und class Schlüsselwörter. class kann durch das Schlüsselwort typename ersetzt werden. Hierbei gibt es keinen Unterschied zwischen typename und class. Die Parameter innerhalb von <> heißen Template-Parameter. Template-Parameter sind Funktionsparametern sehr ähnlich. Template-Parameter dürfen nicht leer sein. Sobald eine Template-Funktion deklariert ist, können Sie die Namen der Template-Parameter verwenden, um Membervariablen und Memberfunktionen in der Klasse zu deklarieren; das heißt, überall dort, wo in dieser Funktion eingebaute Typen verwendet werden können, können die Namen der Template-Parameter verwendet werden. Template-Parameter müssen durch die Template-Argumente initialisiert werden, die beim Aufruf der Template-Funktion angegeben werden. Sobald der Compiler den tatsächlichen Template-Argumenttyp bestimmt, sagt man, dass er eine Instanz des Funktions-Templates instanziiert hat. Zum Beispiel lautet die Template-Form der swap-Funktion:

template <class T> void swap(T& a, T& b){},

Wenn eine solche Template-Funktion aufgerufen wird, wird der Typ T durch den im Aufruf verwendeten Typ ersetzt, zum Beispielswap(a,b)wobeiaundbsindintTyp, und an diesem Punkt werden die Parameter in der Template-Funktion swapTdurchintersetzt, und die Template-Funktion wird zuswap(int &a, int &b). Und wennswap(c,d)wobeicunddJadoubleTyp, wird die Template-Funktion ersetzt durchswap(double &a, double &b), wodurch Code erreicht wird, dessen Funktionsimplementierung typunabhängig ist.

2. Hinweis: Für Funktions-Templates gibt es keinenh(int,int)solchen Aufruf,Der Typ der Template-Parameter kann nicht in den Argumenten eines Funktionsaufrufs angegeben werden. Aufrufe von Funktions-Templates solltenArgumentableitung verwendendurchgeführt werden, das heißt, nur h(2,3)solche Aufrufe können gemacht werden, oderint a, b; h(a,b)。

Beispieldemonstrationen von Funktions-Templates werden unten behandelt!


II. Allgemeine Form von Klassen-Templates

1. Das Format eines Klassen-Templates ist:

template<class  形参名,class 形参名,…>   class 类名
{ ... };

Sowohl Klassen-Templates als auch Funktions-Templates beginnen mit template, gefolgt von einer Template-Parameterliste. Template-Parameter dürfen nicht leer sein. Sobald ein Klassen-Template deklariert ist, kann man die Namen der Parameter des Klassen-Templates verwenden, um Mitgliedsvariablen und Mitgliedsfunktionen in der Klasse zu deklarieren. Das heißt, überall dort, wo in der Klasse eingebaute Typen verwendet werden können, können Template-Parameternamen zur Deklaration verwendet werden. Zum Beispiel

template<class T> class A{public: T a; T b; T hy(T c, T &d);};

In Klasse A werden zwei Mitgliedsvariablen a und b vom Typ T deklariert, und außerdem wird eine Funktion hy mit Rückgabetyp T und zwei Parametern vom Typ T deklariert.

2. Erstellen eines Klassen-Template-Objekts: Zum Beispiel eine Template-KlasseA, dann ist die Methode zum Erstellen eines Objekts unter Verwendung des Klassen-TemplatesA<int> m;Nach der KlasseA, gefolgt von einer<>spitzen Klammer, und füllen Sie die entsprechenden Typen darin ein. Auf diese Weise wird die KlasseAüberall dort, wo Template-Parameter in der Klasse verwendet werden, werden durchintersetzt. Wenn ein Klassen-Template zwei Template-Parameter hat, ist die Methode zum Erstellen eines ObjektsA<int, double> m;Trennen Sie die Typen durch Kommas.

3. Bei Klassentemplates müssen die Typen der Template-Parameter explizit in den spitzen Klammern nach dem Klassennamen angegeben werden. Zum Beispiel ist es falsch,A<2> m;den Template-Parameter aufintzu setzen (Kompilierfehler: error C2079: 'a' uses undefined class 'A<int>'),Klassen-Template-Parameter haben nicht das Problem der Argumentableitung.Das heißt, ein ganzzahliger Wert kann nicht2alsintTyp abgeleitet und an den Template-Parameter übergeben werden. Um den Klassen-Template-Parameter aufintTyp, muss er so angegeben werdenA<int> m。

4. Die Methode zum Definieren von Memberfunktionen außerhalb eines Klassentemplates ist:

template<模板形参列表> 函数返回类型 类名<模板形参名>::函数名(参数列表){函数体},

Wenn beispielsweise eine Klasse A mit zwei Template-Parametern T1 und T2 eine void h()-Funktion enthält, lautet die Syntax zum Definieren dieser Funktion:

template<class T1,class T2> void A<T1,T2>::h(){}。

Hinweis:Beim Definieren von Klassen-Membern außerhalb der Klasse müssen die Template-Parameter nach template mit den Template-Parametern der zu definierenden Klasse übereinstimmen.

5. Nochmal zur Erinnerung: Die Deklaration oder Definition eines Templates kann nur im globalen, Namespace- oder Klassenbereich erfolgen. Das heißt, sie kann nicht im lokalen Bereich oder innerhalb einer Funktion erfolgen; zum Beispiel kann sie nicht in dermainFunktion deklariert oder definiert werden.


III. Template-Parameter

Es gibt drei Arten von Template-Parametern: Typparameter, Nicht-Typparameter und Template-Parameter.

1. Typparameter

1.1 Typ-Template-Parameter:Ein Typparameter besteht aus dem Schlüsselwort class oder typename, gefolgt von einem Spezifizierer, wie z. B.template<class T> void h(T a){}; wobeiTein Typparameter ist. Der Name des Typparameters wird vom Benutzer selbst festgelegt.Ein Template-Parameter repräsentiert einen unbekannten Typ.Ein Template-Typparameter kann überall im Template als Typspezifizierer verwendet werden, genau so wie eingebaute Typspezifizierer oder Klassentypspezifizierer; das heißt, er kann verwendet werden, um Rückgabetypen, Variablendeklarationen usw. anzugeben.

Originalversion des Autors:1.2, Man kann nicht zwei verschiedene Typen für denselben Template-Typ-Parameter angeben. Zum Beispieltemplate<class T>void h(T a, T b){},die aufrufende Anweisungh(2, 3.2)wird einen Fehler verursachen, weil die Anweisung für denselben Template-ParameterTzwei Typen angibt. Das erste Argument2legt den Template-Parameter T alsintfest, während das zweite Argument3.2den Template-Parameter alsdouble, die beiden Parametertypen sind inkonsistent, was einen Fehler verursacht.(Gilt für Funktionstemplates)

Originalversion des Autors: 1.2 ist für Funktionstemplates korrekt, berücksichtigt aber Klassentemplates nicht. Im Folgenden wird die Situation für Klassentemplates ergänzt.

Meine ergänzte Version 1.2 (für Klassentemplates):Wenn wir ein Klassenobjekt deklarieren als:A<int> a, zum Beispieltemplate<class T>T g(T a, T b){}, eine Aufrufanweisunga.g(2, 3.2)verursacht zur Compile-Zeit keinen Fehler, aber es wird eine Warnung geben, weil beim Deklarieren des Klassenobjekts bereitsTin denint-Typ umgewandelt wurde, während das zweite Argument3.2den Template-Parameter alsdouble, zur Laufzeit wird es3.2eine erzwungene Typkonvertierung zu3durchführen. Wenn wir das Objekt der Klasse deklarieren als:A<double> a, dann gibt es diesmal keine solche Warnung, weil vonintzudoubleist eine automatische Typkonvertierung.

Demonstrationsbeispiel 1:

TemplateDemo.h

#ifndef TEMPLATE_DEMO_HXX #define TEMPLATE_DEMO_HXX template<class T> class A{ public: T g(T a,T b); A(); }; #endif   TemplateDemo.cpp #include<iostream.h> #include "TemplateDemo.h" template<class T> A<T>::A(){} template<class T> T A<T>::g(T a,T b){ return a+b; } void main(){ A<int> a; cout<<a.g(2,3.2)<<endl; }

Kompilierungsergebnis:

--------------------Configuration: TemplateDemo - Win32 Debug--------------------
Compiling...
TemplateDemo.cpp
G:\C++\CDaima\TemplateDemo\TemplateDemo.cpp(12) : warning C4244: 'argument' : conversion from 'const double' to 'int', possible loss of data

TemplateDemo.obj - 0 error(s), 1 warning(s)

Run result:5 

 

Aus dem obigen Testbeispiel können wir ersehen, dass es nicht so rigoros ist wie im Originalwerk des Autors! Dies ist nur mein persönliches Testergebnis! Bitte nehmen Sie eine praktische und wahrheitssuchende Haltung ein und überprüfen Sie es selbst!

2. Nicht-Typ-Parameter

2.1 Nicht-Typ-Template-Parameter:Ein Nicht-Typ-Template-Parameter ist ein Parameter eines eingebauten Typs, wie zum Beispieltemplate<class T, int a> class B{};wobeiint aist ein Nicht-Typ-Template-Parameter.

2.2 Nicht-Typ-Parameter sind konstante Werte innerhalb der Template-Definition; das heißt, Nicht-Typ-Parameter sind Konstanten innerhalb des Templates.

    2.3, Nicht-Typ-Template-Parameter können nur integrale Typen, Zeiger und Referenzen sein, Typen wie double, String und String** sind nicht erlaubt.Aberdouble &,double *,Referenzen oder Zeiger auf Objekte sind gültig.

    2.4, Das Argument für einen Nicht-Typ-Template-Parameter muss ein konstanter Ausdruck sein,das heißt, es muss zur Kompilierzeit berechenbar sein.

2.5 Hinweis:Jedes lokale Objekt, jede lokale Variable, die Adresse eines lokalen Objekts oder die Adresse einer lokalen Variablen ist kein konstanter Ausdruck und kann nicht als Argument für einen Nicht-Typ-Template-Parameter verwendet werden.Globale Zeigertypen, globale Variablen und globale Objekte sind ebenfalls keine konstanten Ausdrücke und können nicht als Argumente für Nicht-Typ-Template-Parameter verwendet werden.

    2.6, ; A<b> m; das heißt, Array-zu-Zeiger-Konvertierung.。

    2.7 、sizeofDas Ergebnis eines Ausdrucks ist ein konstanter Ausdruck und kann auch als Argument für einen Nicht-Typ-Template-Parameter verwendet werden.

2.8. Wenn der Template-Parameter ein Ganzzahltyp ist, muss das beim Aufrufen des Templates verwendete Argument vom Ganzzahltyp sein und während der Kompilierung eine Konstante sein. Zum Beispiel,template <class T, int a> class A{};Wenn esint b gibt, dann A<int, b> m;verursacht einen Fehler, weilbb keine Konstante ist. Wennconst int b, dann ist A<int, b> m; korrekt, weil zu diesem Zeitpunktbb eine Konstante ist.

    2.9 、Nicht-Typ-Parameter werden im Allgemeinen nicht in Funktions-Templates verwendet.Zum Beispiel gibt es ein Funktions-Templatetemplate<class T, int a> void h(T b){}, wenn manh(2)zum Aufruf verwendet, tritt ein Fehler auf, der anzeigt, dass das Argument für den Nicht-Typ-Parameter a nicht abgeleitet werden kann. Für eine solche Template-Funktion können explizite Template-Argumente verwendet werden, um das Problem zu lösen, wie z. B. h<int, 3>(2), wodurch der Nicht-Typ-Parameter a auf die Ganzzahl 3 gesetzt wird. Explizite Template-Argumente werden später eingeführt.

2.10. Zulässige Konvertierungen zwischen Parameter und Argument von Nicht-Typ-Template-Parametern
1. Konvertierungen von Array zu Zeiger und von Funktion zu Zeiger sind zulässig. Zum Beispiel: template <int *a> class A{}; int b
2. Konvertierung des const-Qualifizierers. Zum Beispiel: template<const int *a> class A{}; int b; A<&b> m; das heißt, Konvertierung von int * zu const int *.
3. Promotion-Konvertierung. Zum Beispiel: template<int a> class A{}; const short b=2; A<b> m; das heißt, Promotion-Konvertierung von short nach int.
4. Integral-Konvertierung. Zum Beispiel: template<unsigned int a> class A{}; A<3> m; das heißt, Konvertierung von int nach unsigned int.
5. Übliche Konvertierungen.

Beispiel 1 zur Demonstration von Nicht-Typ-Parametern:

  Der Benutzer legt die Größe des Stacks selbst fest und implementiert die zugehörigen Operationen des Stacks.

TemplateDemo.h

#ifndef TEMPLATE_DEMO_HXX #define TEMPLATE_DEMO_HXX template<class T,int MAXSIZE> class Stack{//MAXSIZE wird vom Benutzer beim Erstellen des Objekts festgelegt private: T elems[MAXSIZE]; // Array mit Elementen int numElems; // Aktuelle Gesamtzahl der Elemente public: Stack(); // Konstruktor void push(T const&); // Element einfügen void pop(); // Element entfernen T top() const; // Gibt das oberste Element des Stacks zurück bool empty() const{ // Gibt zurück, ob der Stack leer ist return numElems == 0; } bool full() const{ // Gibt zurück, ob der Stack voll ist return numElems == MAXSIZE; } }; template <class T,int MAXSIZE> Stack<T,MAXSIZE>::Stack():numElems(0){ // Anfangs enthält der Stack keine Elemente // Nichts tun } template <class T,int MAXSIZE> void Stack<T, MAXSIZE>::push(T const& elem){ if(numElems == MAXSIZE){ throw std::out_of_range("Stack<>::push(): Stack ist voll"); } elems[numElems] = elem; // Element anhängen ++numElems; // Anzahl der Elemente erhöhen } template<class T,int MAXSIZE> void Stack<T,MAXSIZE>::pop(){ if (numElems <= 0) { throw std::out_of_range("Stack<>::pop(): leerer Stack"); } --numElems; // Anzahl der Elemente verringern } template <class T,int MAXSIZE> T Stack<T,MAXSIZE>::top()const{ if (numElems <= 0) { throw std::out_of_range("Stack<>::top(): leerer Stack"); } return elems[numElems-1]; // Das letzte Element zurückgeben } #endif

TemplateDemo.cpp

#include<iostream.h> #include <iostream> #include <string> #include <cstdlib> #include "TemplateDemo.h" int main(){ try { Stack<int,20> int20Stack; // Ein Stack, der 20 int-Elemente speichern kann Stack<int,40> int40Stack; // Ein Stack, der 40 int-Elemente speichern kann Stack<std::string,40> stringStack; // Ein Stack, der 40 string-Elemente speichern kann // Verwende einen Stack, der 20 int-Elemente speichern kann int20Stack.push(7); std::cout << int20Stack.top() << std::endl; //7 int20Stack.pop(); // Verwende einen Stack, der 40 string-Elemente speichern kann stringStack.push("hello"); std::cout << stringStack.top() << std::endl; //hello stringStack.pop(); stringStack.pop(); //Ausnahme: Stack<>::pop<>: leerer Stack return 0; } catch (std::exception const& ex) { std::cerr << "Ausnahme: " << ex.what() << std::endl; return EXIT_FAILURE; // Das Programm mit einem Fehler-Flag beenden } }

Demonstrationsbeispiel 2 für Nicht-Typ-Parameter:

TemplateDemo01.h

#ifndef TEMPLATE_DEMO_O1 #define TEMPLATE_DEMO_01 template<typename T> class CompareDemo{ public: int compare(const T&, const T&); }; template<typename T> int CompareDemo<T>::compare(const T& a,const T& b){ if((a-b)>0) return 1; else if((a-b)<0) return -1; else return 0; } #endif

TemplateDemo01.cpp

#include<iostream.h> #include "TemplateDemo01.h" void main(){ CompareDemo<int> cd; cout<<cd.compare(2,3)<<endl; }

Run result:-1 

TemplateDemo01.cpp

#include<iostream.h> #include "TemplateDemo01.h" void main(){ CompareDemo<double> cd; cout<<cd.compare(3.2,3.1)<<endl; }

Run result:1 

 

TemplateDemo01.h

#ifndef TEMPLATE_DEMO_O1 #define TEMPLATE_DEMO_01 template<typename T> class CompareDemo{ public: int compare(T&, T&); }; template<typename T> int CompareDemo<T>::compare(T& a,T& b){ if((a-b)>0) return 1; else if((a-b)<0) return -1; else return 0; } #endif

TempalteDemo01.cpp

#include<iostream.h> #include "TemplateDemo01.h" void main(){ CompareDemo<int> cd; int a=2,b=3; cout<<cd.compare(a,b)<<endl; }

Demonstrationsbeispiel 3 für Nicht-Typ-Parameter:

TemplateDemo02.cpp

#include<iostream.h> template<typename T> const T& max(const T& a,const T& b){ return a>b ? a:b; } void main(){ cout<<max(2.1,2.2)<<endl;// Template-Argument wird implizit als double abgeleitet cout<<max<double>(2.1,2.2)<<endl;// Template-Parameter explizit angeben. cout<<max<int>(2.1,2.2)<<endl;// Explizit angegebene Template-Parameter konvertieren den Funktionsparameter direkt in int. }

Run result:

cout<<max<int>(2.1,2.2)<<endl; //显示指定的模板参数,会将函数函数直接转换为int.

Diese Anweisung verursacht eine Warnung:

--------------------Configuration: TemplateDemo02 - Win32 Debug--------------------
Compiling...
TemplateDemo02.cpp
G:\C++\CDaima\TemplateDemo02\TemplateDemo02.cpp(11) : 
  warning C4244: 'argument' : conversion from 'const double' to 'const int', possible loss of data
G:\C++\CDaima\TemplateDemo02\TemplateDemo02.cpp(11) : 
  warning C4244: 'argument' : conversion from 'const double' to 'const int', possible loss of data

TemplateDemo02.obj - 0 error(s), 2 warning(s)

IV. Standard-Template-Typparameter von Klassentemplates

1, Für die Typparameter von Klassentemplates können Standardwerte angegeben werden, für die Typparameter von Funktionstemplates jedoch nicht.Sowohl Funktions- als auch Klassentemplates können Standardwerte für Nicht-Typ-Template-Parameter bereitstellen.

2. Die Form von Standardwerten für Klassentemplate-Typparameter ist:template<class T1, class T2=int> class A{};für den zweiten Template-TypparameterT2geben Sieinteinen Standardwert vom Typ ... an

3. Standardwerte für Klassentemplate-Typparameter sind dasselbe wie Standardparameter von Funktionen. Wenn es mehrere Typparameter gibt, dann müssen alle Template-Parameter nach dem ersten Parameter, der einen Standardwert hat, ebenfalls Standardwerte haben. Zum Beispiel,template<class T1=int, class T2>class A{};es ist falsch, weilT1ein Standardwert für ... angegeben ist, währendT2kein Standardwert für ... festgelegt ist

4. Wenn man Member einer Klasse außerhalb des Klassentemplates definiert,templatesollte die Parameterliste nach 'template' die Standardparametertypen weglassen. Zum Beispiel:template<class T1, class T2=int> class A{public: void h();}; die Definitionsmethode isttemplate<class T1,class T2> void A<T1,T2>::h(){}。

Klassentemplate-Typparameter definieren:

Demonstrationsbeispiel 1:

TemplateDemo.h

#ifndef TEMPLATE_DEMO_HXX #define TEMPLATE_DEMO_HXX template<class T> class A{ public: T g(T a,T b); A(); }; #endif

TemplateDemo.cpp

#include<iostream.h> #include "TemplateDemo.h" template<class T> A<T>::A(){} template<class T> T A<T>::g(T a,T b){ return a+b; } void main(){ A<int> a; cout<<a.g(2,3)<<endl; }

Run result:5

Beispiel 1 für Standard-Template-Typparameter bei Klassentemplates:

TemplateDemo03.h

#ifndef TEMPLATE_DEMO_03 #define TEMPLATE_DEMO_03 // Definieren Sie ein Klassentemplate mit einem Standardtypparameter. Hier ist T2 standardmäßig auf int gesetzt. template<class T1,class T2=int> class CeilDemo{ public: int ceil(T1,T2); }; // Wenn man Member der Klasse außerhalb des Klassentemplates definiert, sollte die Parameterliste nach 'template' die Standardparametertypen weglassen. template<class T1,class T2> int CeilDemo<T1,T2>::ceil(T1 a,T2 b){ return a>>b; } #endif

TemplateDemo03.cpp

#include<iostream.h> #include "TemplateDemo03.h" void main(){ CeilDemo<int> cd; cout<<cd.ceil(8,2)<<endl; }

Run result:2 

Wenn man Member der Klasse außerhalb des Klassentemplates definiert, sollte die Parameterliste nach 'template' die Standardparametertypen weglassen. Wenn sie nicht weggelassen werden, verursacht das keinen Kompilierungsfehler, sondern gibt eine Warnung aus:

--------------------Configuration: TemplateDemo03 - Win32 Debug--------------------
Compiling...
TemplateDemo03.cpp
g:\c++\cdaima\templatedemo03\templatedemo03.h(12) : 
warning C4519: default template arguments are only allowed on a class template; ignored

TemplateDemo03.obj - 0 error(s), 1 warning(s)

Originalautor:Standardwerte für Typparameter von Klassentemplates sind dieselben wie Standardparameter von Funktionen. Wenn es mehrere Typparameter gibt, müssen alle Templateparameter nach dem ersten Parameter, der einen Standardwert hat, ebenfalls Standardwerte haben. Zum Beispiel:template<class T1=int, class T2>class A{};es ist falsch, weilT1ein Standardwert für ... angegeben ist, währendT2kein Standardwert für ... festgelegt ist

Beispiel 2 zu Standard-Template-Typparametern für Klassentemplates:

TemplateDemo03.h

#ifndef TEMPLATE_DEMO_03 #define TEMPLATE_DEMO_03 template<class T1=int,class T2,class T3> class CeilDemo{ public: int ceil(T1,T2,T3); }; template<class T1,class T2,class T3> int CeilDemo<T1,T2,T3>::ceil(T1 a,T2 b,T3 c){ return a+b+c; } #endif

TemplateDemo03.cpp

#include<iostream.h> #include "TemplateDemo03.h" void main(){ CeilDemo<int,int> cd; cout<<cd.ceil(2,3,4)<<endl; }

Run result:9  

TemplateDemo03.h

#ifndef TEMPLATE_DEMO_03 #define TEMPLATE_DEMO_03 template<class T1=int,class T2,class T3> class CeilDemo{ public: double ceil(T1,T2,T3); }; template<class T1,class T2,class T3> double CeilDemo<T1,T2,T3>::ceil(T1 a,T2 b,T3 c){ return a+b+c; } #endif

TemplateDemo03.cpp

#include<iostream.h> #include "TemplateDemo03.h" void main(){ CeilDemo<double,double> cd; cout<<cd.ceil(2,3.1,4.1)<<endl; }

Kompilierungsfehler:

--------------------Configuration: TemplateDemo03 - Win32 Debug--------------------
Compiling...
TemplateDemo03.cpp
g:\c++\cdaima\templatedemo03\templatedemo03.h(12) : 
error C2244: 'CeilDemo<T1,T2,T3>::ceil' : unable to resolve function overload
g:\c++\cdaima\templatedemo03\templatedemo03.cpp(6) : 
error C2065: 'cd' : undeclared identifier
g:\c++\cdaima\templatedemo03\templatedemo03.cpp(6) : 
error C2228: left of '.ceil' must have class/struct/union type
Error executing cl.exe.

TemplateDemo03.obj - 3 error(s), 0 warning(s)

Aus dem obigen Beispiel können wir sehen, dass ein Fehler auftritt, wenn wir versuchen, T2 und T3 als Typ double zu definieren (T1 ist standardmäßig als int definiert). Wenn wir also der Aussage des Autors folgen und T2 und T3 ebenfalls auf den Standardwert double setzen, tritt der Fehler dann immer noch auf? Betrachten Sie das folgende Beispiel:

Beispiel 4 zu Standard-Template-Typparametern für Klassentemplates:

TemplateDemo03.h

#ifndef TEMPLATE_DEMO_03 #define TEMPLATE_DEMO_03 template<class T1=int,class T2=double,class T3=double> class CeilDemo{ public: double ceil(T1,T2,T3); }; template<class T1,class T2,class T3> double CeilDemo<T1,T2,T3>::ceil(T1 a,T2 b,T3 c){ return a+b+c; } #endif

TemplateDemo03.cpp

#include<iostream.h> #include "TemplateDemo03.h" void main(){ CeilDemo<int,double,double> cd; cout<<cd.ceil(2,3.1,4.1)<<endl; }

Kompilierungsfehler:

--------------------Configuration: TemplateDemo03 - Win32 Debug--------------------
Compiling...
TemplateDemo03.cpp
g:\c++\cdaima\templatedemo03\templatedemo03.h(12) : 
error C2244: 'CeilDemo<T1,T2,T3>::ceil' : unable to resolve function overload
g:\c++\cdaima\templatedemo03\templatedemo03.cpp(6) : 
error C2065: 'cd' : undeclared identifier
g:\c++\cdaima\templatedemo03\templatedemo03.cpp(6) : 
error C2228: left of '.ceil' must have class/struct/union type
Error executing cl.exe.

TemplateDemo03.obj - 3 error(s), 0 warning(s)

An den Ergebnissen können wir sehen, dass es derselbe Fehler wie im vorherigen Beispiel ist. Aus den Beispielen können wir Folgendes zusammenfassen: Wenn ein Klassentemplate mehrere Typparameter hat und Standardwerte für Typparameter verwendet werden, sollten sie so weit wie möglich am Ende der Parameterliste stehen, und die Standardparametertypen müssen gleich sein. Wenn für den ersten Parameter ein Standardwert festgelegt ist, müssen auch alle Template-Parameter danach auf Standardwerte desselben Typs wie der erste Parameter gesetzt werden.(Erklärung: Ich bin auch neu in C++. Das Obige sind nur einige Fragen, die ich nach Demonstrationen mit Beispielen zum ursprünglichen Autor gestellt habe. Meine Beispiele mögen Mängel haben. Ich hoffe, dass Experten nicht zögern, Ratschläge zu geben, damit wir diesen Blog gemeinsam verbessern und eine Lernplattform für Anfänger wie mich bieten können!)

Als Nächstes prüfen wir: "Für Typparameter von Funktions-Templates können keine Standardwerte angegeben werden":

Beispiel 5 für Standard-Typparameter von Klassentemplates:

TemplateDemo04.cpp

#include<iostream.h> template<class T1,class T2,class T3> T1 sum(T1 a,T2 b,T3 c=int){ return a+b+c; } void main(){ cout<<sum<double,double>(1.1,2.1,3)<<endl; }

Kompilierungsfehler:

--------------------Configuration: TemplateDemo04 - Win32 Debug--------------------
Compiling...
TemplateDemo04.cpp
g:\c++\cdaima\templatedemo04\templatedemo04.cpp(4) : 
error C2062: type 'int' unexpected
Error executing cl.exe.

TemplateDemo04.obj - 1 error(s), 0 warning(s)

Geänderte TemplateDemo.cpp

TemplateDemo.cpp

#include<iostream.h> template<class T1,class T2,class T3> T1 sum(T1 a,T2 b,T3 c){ return a+b+c; } void main(){ cout<<sum<double,short,int>(1.1,3,257)<<endl; }

Run result:261.1 

    

Das Demonstrationsbeispiel des ursprünglichen Autors lautet wie folgt:

Beispiel für Nicht-Typ-Parameter in Klassentemplates

// Die Deklaration oder Definition eines Templates kann nur im globalen Bereich, im Namespace-Bereich oder im Klassenbereich erfolgen. Das heißt, sie kann nicht im lokalen Bereich oder innerhalb einer Funktion erfolgen; zum Beispiel kann ein Template nicht in der main-Funktion deklariert oder definiert werden. // Definition eines Klassentemplates template<class T>class A{public:T g(T a, T b); A();}; // Klasse A mit einem Klassentemplate-Typparameter T definieren template<class T1,class T2>class B{public:void g();}; // Klasse B mit zwei Klassentemplate-Typparametern T1, T2 definieren // Standard-Typparameter für Klassentemplates definieren; Standard-Typparameter eignen sich nicht für Funktionstemplates. template<class T1,class T2=int> class D{public: voidg();}; // Ein Klassentemplate mit Standard-Typparametern definieren. Hier ist T2 standardmäßig auf int gesetzt. //template<class T1=int, class T2>class E{}; // Fehler: Wenn für T1 ein Standard-Typparameter festgelegt wird, müssen alle Parameter nach T1 ebenfalls Standardwerte haben. // Im Folgenden finden sich Definitionen von Nicht-Typ-Parametern // Nicht-Typ-Parameter können nur integrale Typen, Zeiger und Referenzen sein. Typen wie double, String, String ** sind nicht zulässig. Jedoch sind double &, double * (Referenzen oder Zeiger auf Objekte)korrekt.template<class T1,int a> class Ci{public:void g();}; // Definiere einen Nicht-Typ-Template-Parameter, wobei der Parameter ein integraler Typ ist template<class T1,int &a>class Cip{public:void g();}; template<class T1,A<int>* m> class Cc{public:void g();}; // Definiere einen Nicht-Typ-Template-Parameter, wobei der Parameter ein Zeiger auf ein Objekt der Klasse A vom Typ int ist. template<class T1,double*a>class Cd{public:void g();}; // Definiere einen Nicht-Typ-Template-Parameter, wobei der Parameter eine Referenz vom Typ double ist. class E{}; template<class T1,E &m> class Ce{}; // Ein Nicht-Typ-Template-Parameter ist eine Referenz auf ein Objekt. // Die folgenden Deklarationen von Nicht-Typ-Parametern sind falsch. //template<class T1,A m>class Cc{}; // Fehler: Ein Objekt kann nicht als Nicht-Typ-Parameter verwendet werden; der Typ eines Nicht-Typ-Template-Parameters kann nur eine Referenz oder ein Zeiger auf ein Objekt sein. //template<class T1,double a>class Cc{}; // Fehler: Der Parameter eines Nicht-Typ-Templates kann nicht vom Typ double sein; er kann eine Referenz auf double sein. //template<class T1,A<int> m>class Cc{}; // Fehler: Der Parameter eines Nicht-Typ-Templates kann kein Objekt sein; er muss eine Referenz oder ein Zeiger auf ein Objekt sein. Diese Regel gilt auch für Template-Parameterohne Ausnahme.// Methoden zum Definieren verschiedener Klassenmember außerhalb des Klassentemplates, //Die Funktion von typeid(Variablenname).name() besteht darin, den Typ des Variablennamens zu extrahieren. Zum Beispiel int a, dann gibt cout<<typeid(a).name() int aus. template<class T> A<T>::A(){cout<<"class A goucao"<<typeid(T).name()<<endl;} //Methode zum Definieren eines Klassenkonstruktors außerhalb des Klassentemplates template<class T> T A<T>::g(T a,T b){cout<<"class A g(T a,T b)"<<endl;} //Definieren von Membern eines Klassentemplates außerhalb des Klassentemplates template<class T1,class T2> voidB<T1,T2>::g(){cout<<"class g f()"<<typeid(T1).name()<<typeid(T2).name()<<endl;} //Wenn Klassenmember außerhalb der Klasse definiert werden, sollten die Template-Parameter nach template mit den Template-Parametern der zu definierenden Klasse übereinstimmen. template<class T1,int a> voidCi<T1,a>::g(){cout<<"class Ci g()"<<typeid(T1).name()<<endl;} template<class T1,int &a> voidCip<T1,a>::g(){cout<<"class Cip g()"<<typeid(T1).name()<<endl;} //Beim Definieren von Klassenmitgliedern außerhalb der Klasse sollten die Template-Parameter nach template mit den Template-Parametern der zu definierenden Klasse übereinstimmen. template<class T1,A<int> *m> voidCc<T1,m>::g(){cout<<"class Cc g()"<<typeid(T1).name()<<endl;} template<class T1,double* a> voidCd<T1,a>::g(){cout<<"class Cd g()"<<typeid(T1).name()<<endl;} //Vorgehensweise zum Definieren von Membern außerhalb der Klasse für eine Template-Klasse mit Standard-Typparametern. //Beim Definieren von Klassenmitgliedern außerhalb der Klasse sollten Standardwerte in der Template-Parameterliste weggelassen werden. template<class T1,class T2> voidD<T1,T2>::g(){cout<<"class D g()"<<endl;} //template<class T1,class T2=int> void D<T1,T2>::g(){cout<<"class D k()"<<endl;} //Fehler: Wenn man ein Template mit Standardtyp außerhalb des Klassentemplates definiert, muss der formaleParameter, intemplatesollte der Standardwert in der Parameterliste weggelassen werden.//Einige globale Variablen definieren. int e=2; doubleed=2.2; double*pe=&ed; A<int> mw; A<int> *pec=&mw; E me; //Hauptfunktion beginnt int main() { // template<class T>void h(){} //Fehler: Template-Deklarationen oder -Definitionen können nur im globalen, Namensraum- oder Klassenbereich auftreten. Das heißt, sie können nicht im lokalen Gültigkeitsbereich oder innerhalb einer Funktion auftreten. //A<2> m; //Fehler: Für Klassentemplates gibt es keine Argumentdeduktion; der Typ muss explizit in spitzen Klammern angegeben werden. //Beispiel für den Aufruf eines Klassentemplates A<int> ma; //Ausgabe "class A goucao int" erzeugt Objekt ma des Klassentemplates A mit Typ int. B<int,int> mb; mb.g(); //Ausgabe "class B g() int int" erzeugt Objekt mb des Klassentemplates B, wobei die Typparameter T1 und T2 auf int gesetzt sind. //Aufruf eines Nicht-Typ-Parameters //Das Argument für einen Nicht-Typ-Template-Parameter muss ein konstanter Ausdruck sein, d. h., es muss zur Kompilierungszeit berechenbar sein. Jedes lokale Objekt, jede lokale Variable, die Adresse eines lokalen Objekts, lokaleVariablenadressen sind keine konstanten Ausdrücke und können nicht als Argumente für Nicht-Typ-Template-Parameter verwendet werden. Globale Zeigertypen, globale Variablen und globale Objekte sind ebenfalls keine konstanten Ausdrücke und können nicht//Die Adresse oder Referenz einer globalen Variablen, die Adresse oder Referenz eines globalen Objekts oder eine Variable vom Typ const ist ein konstanter Ausdruck und kann als Argument für einen Nicht-Typ-Template-Parameter verwendet werden. //Die Methode für den Aufruf eines int-Nicht-Typ-Parameters heißt Ci, deklariert als template<class T1,int a> class Ci Ci<int,3>//Richtig, der Wert R ist eine int-Konstante, Ausgabe "class Ci g() int" const int a2=3; Ci<int,a2> mci1; mci1.g(); //Richtig, weil a2 hier eine const-Konstante ist. Ausgabe "class Ci g() int" //Ci<int,a> mci; //Fehler, die int-Variable a ist eine lokale Variable und kein konstanter Ausdruck. //Ci<int,e> mci; //Fehler, die globale int-Variable e ist ebenfalls kein konstanter Ausdruck. //Die Methode zum Aufrufen eines int&-Nicht-Typ-Parameters hat den Klassennamen Cip, deklariert als template<class T1,int &a>class Cip Cip<int,e> mcip; //Richtig, die Referenz oder Adresse einer globalen Variablen ist ein konstanter Ausdruck. //Cip<int,a> mcip1; //Fehler, die Referenz oder Adresse einer lokalen Variablen ist kein konstanter Ausdruck. //Die Methode zum Aufrufen eines double*-Nicht-Typ-Parameters hat den Klassennamen Cd, deklariert als template<class T1,double *a>class Cd Cd<int,&ed> mcd; //Richtig, die Referenz oder Adresse einer globalen Variablen ist ein konstanter Ausdruck. //Cd<int,pe> mcd1; //Fehler, ein Zeiger auf eine globale Variable ist kein konstanter Ausdruck. //double dd=3.3; //Fehler, die Adresse einer lokalen Variablen ist kein konstanter Ausdruck und kann nicht als Argument für einen Nicht-Typ-Parameter verwendet werden //Cd<int,&e> mcd; //Fehler, obwohl für Nicht-Typ-Parameter einige Konvertierungen erlaubt sind, kann diese Konvertierung nicht durchgeführt werden. //Die Methode zum Aufrufen eines Template-Typparameter-Objekts A<int>* hat den Klassennamen Cc, deklariert als template<class T1,A<int>* m> class Cc Cc<int,&mw> mcc; mcc.g(); //Richtig, die Adresse oder Referenz eines globalen Objekts ist ein konstanter Ausdruck. //Cc<int,&ma> mcc; //Fehler, die Adresse oder Referenz einer lokalen Variablen ist kein konstanter Ausdruck. //Cc<int,pec> mcc2; //Fehler, ein Zeiger auf ein globales Objekt ist kein konstanter Ausdruck. //Die Methode zum Aufrufen eines Nicht-Typ-Parameters, der eine Referenz auf ein E&-Objekt ist, hat den Klassennamen Ce. Deklarationsform: template<class T1,E &m> class Ce E me1; //Ce<int,me1> mce1; //Fehler, ein lokales Objekt ist kein konstanter Ausdruck Ce<int,me> mce; //Korrekt, der Zeiger oder die Referenz auf ein globales Objekt ist ein konstanter Ausdruck. //Beispiel für Konvertierungen für Nicht-Typ-Parameter, Klassenname Ci //Nicht-Typ-Parameter erlauben Array-zu-Zeiger-, Funktion-zu-Zeiger-, const-Qualifizierer-Konvertierungen, Promotion-Konvertierungen, integrale Konvertierungen und übliche Konvertierungen. const short s=3; Ci<int,s> mci4†//Korrekt, obwohl short und int nicht exakt übereinstimmen, kann short hier in int umgewandelt werden.

Ursprüngliche Adresse:

https://www.cnblogs.com/gw811/archive/2012/10/25/2738929.html

http://www.cnblogs.com/gw811/archive/2012/10/25/2736224.html