テンプレートはC++がパラメータ化多態性をサポートするためのツールです。テンプレートを使用すると、ユーザーはクラスまたは関数に対して一般的なパターンを宣言でき、クラス内の一部のデータメンバまたはメンバ関数のパラメータ、戻り値を任意の型にすることができます。

テンプレートは型をパラメータ化するツールです。

通常、関数テンプレートとクラステンプレートの2つの形式があります。

関数テンプレートは、パラメータの型だけが異なる関数を対象とします。

クラステンプレートは、データメンバとメンバ関数の型だけが異なるクラスを対象とします。

テンプレートを使用する目的は、プログラマが型に依存しないコードを書けるようにすることです。例えば、2つのint型を交換するswap関数を書いた場合、この関数はint型にしか対応できず、doubleやcharなどの型には対応できません。これらの型の交換を実現するには、別のswap関数を書き直す必要があります。テンプレートを使用する目的は、プログラムの実装を型に依存しないようにすることです。例えば、swapテンプレート関数はint型の交換もdouble型の交換も実現できます。テンプレートは関数とクラスに適用できます。以下でそれぞれ説明します。

注意:テンプレートの宣言または定義は、グローバル、名前空間、またはクラスのスコープ内でのみ行えます。つまり、ローカルスコープや関数内では行えません。例えば、main関数内でテンプレートを宣言または定義することはできません。


一、関数テンプレートの一般形式

1、関数テンプレートの形式:

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

ここでtemplateとclassはキーワードです。classはtypenameキーワードで置き換えることができます。ここではtypenameとclassに違いはありません。<>括弧内のパラメータをテンプレート仮引数と呼びます。テンプレート仮引数は関数仮引数とよく似ていますが、テンプレート仮引数を空にすることはできません。テンプレート関数を宣言すると、テンプレート関数の仮引数名を使用してクラス内のメンバ変数やメンバ関数を宣言できます。つまり、その関数内で組み込み型を使用できる場所ならどこでもテンプレート仮引数名を使用できます。テンプレート仮引数は、そのテンプレート関数を呼び出すときに提供されるテンプレート実引数によって初期化する必要があります。コンパイラが実際のテンプレート実引数の型を決定すると、関数テンプレートのインスタンスをインスタンス化したと呼びます。例えば、swapテンプレート関数の形式は次のとおりです:

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

このようなテンプレート関数を呼び出すとき、型Tは呼び出し時の型に置き換えられます。例えばswap(a,b)ここでaとbであるint型の場合、テンプレート関数swap内の仮引数はTによって置き換えられ、intテンプレート関数は次のようになります:swap(int &a, int &b)。一方、swap(c,d)ここでcとdであるdouble型の場合、テンプレート関数は次のように置き換えられます:swap(double &a, double &b)これにより、関数の実装が型に依存しないコードを実現できます。

2、注意:関数テンプレートに関しては、次のような呼び出しは存在しません:h(int,int)このような呼び出しはありません。関数呼び出しの引数でテンプレート仮引数の型を指定することはできません。関数テンプレートの呼び出しは、実引数推論を使用して行うべきです。つまり、次のような呼び出しのみ可能です: h(2,3)このような呼び出し、またはint a, b; h(a,b)。

関数テンプレートの具体例は後述します!


二、クラステンプレートの一般形式

1、クラステンプレートの形式は次のとおりです:

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

クラステンプレートと関数テンプレートはどちらもtemplateで始まり、その後ろにテンプレート仮引数リストが続きます。テンプレート仮引数は空にできません。クラステンプレートを宣言すると、クラステンプレートの仮引数名を使用してクラス内のメンバ変数とメンバ関数を宣言できます。つまり、クラス内で組み込み型を使用できる場所ならどこでもテンプレート仮引数名を使用して宣言できます。例えば

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

クラスA内に、型がTの2つのメンバ変数aとbを宣言し、戻り値の型がTで、2つの引数の型がTである関数hyも宣言しています。

2、クラステンプレートオブジェクトの作成:例えば、テンプレートクラスAがある場合、クラステンプレートを使用してオブジェクトを作成する方法は次のとおりです:A<int> m;クラスAの後ろに<>山括弧を付け、その中に対応する型を記入します。これにより、クラスA内でテンプレート仮引数を使用しているすべての場所がintによって置き換えられます。クラステンプレートに2つのテンプレート仮引数がある場合、オブジェクトを作成する方法は次のとおりです:A<int, double> m;型の間はカンマで区切ります。

3、クラステンプレートの場合、テンプレート仮引数の型はクラス名の後ろの山括弧内で明示的に指定する必要があります。例えばA<2> m;この方法でテンプレート仮引数をintに設定するのは誤りです(コンパイルエラー:error C2079: 'a' uses undefined class 'A<int>'),クラステンプレートの仮引数には実引数推論の問題はありません。つまり、整数値2をint型としてテンプレート仮引数に推論して渡すことはできません。クラステンプレートの仮引数をint型に設定するには、次のように指定する必要があります:A<int> m。

4、クラステンプレートの外部でメンバ関数を定義する方法は次のとおりです:

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

例えば、2つのテンプレート仮引数T1、T2を持つクラスAにvoid h()関数が含まれている場合、その関数を定義する構文は次のとおりです:

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

注意:クラスの外でクラスのメンバを定義する場合、templateの後ろのテンプレート仮引数は、定義しようとするクラスのテンプレート仮引数と一致している必要があります。

5、再度注意を促します:テンプレートの宣言または定義は、グローバル、名前空間、またはクラスのスコープ内でのみ行えます。つまり、ローカルスコープや関数内では行えません。例えば、main関数内でテンプレートを宣言または定義することはできません。


三、テンプレートの仮引数

テンプレート仮引数には3つの型があります:型仮引数、非型仮引数、テンプレート仮引数です。

1、型仮引数

1.1、型テンプレート仮引数:型仮引数は、キーワードclassまたはtypenameの後ろに指定子を続けて構成されます。例えばtemplate<class T> void h(T a){};ここでTは型仮引数であり、型仮引数の名前はユーザー自身が決定します。テンプレート仮引数は未知の型を表します。テンプレート型仮引数は、型指定子としてテンプレート内のどこでも使用でき、組み込み型指定子やクラス型指定子とまったく同じように使用できます。つまり、戻り値の型の指定や変数の宣言などに使用できます。

著者オリジナル版:1.2、同じテンプレート型仮引数に2つの異なる型を指定することはできません。例えばtemplate<class T>void h(T a, T b){},文の呼び出しh(2, 3.2)はエラーになります。なぜなら、その文は同じテンプレート仮引数Tに2つの型を指定しているからです。最初の実引数2はテンプレート仮引数Tをintに指定し、2番目の実引数3.2はテンプレート仮引数をdoubleに指定しています。2つの型の仮引数が一致しないため、エラーになります。(関数テンプレートに関するもの)

著者オリジナル版:1.2は関数テンプレートに対しては正しいですが、クラステンプレートを無視しています。以下でクラステンプレートの場合を補足します。

筆者による1.2補足版(クラステンプレート向け)、クラスオブジェクトを次のように宣言した場合:A<int> a、例えばtemplate<class T>T g(T a, T b){}、文の呼び出しa.g(2, 3.2)はコンパイル時にはエラーになりませんが、警告が出ます。なぜなら、クラスオブジェクトを宣言するときに既にTをint型に変換しており、2番目の実引数3.2はテンプレート仮引数をdoubleに指定しているため、実行時に3.2が3に強制型変換されます。クラスのオブジェクトを次のように宣言した場合:A<double> a、このときは上記の警告はありません。なぜならint到doubleは自動型変換だからです。

デモ例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; }

コンパイル結果:

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

実行結果:5 

 

上記のテスト例からわかるように、著者のオリジナル版ほど厳密ではありません!ここは筆者の個人的なテスト結果に過ぎません!皆さんには事実を重んじる態度で、ご自身で検証していただきたいと思います!

2、非型仮引数

2.1、非型テンプレート仮引数:テンプレートの非型仮引数は、組み込み型の仮引数です。例えばtemplate<class T, int a> class B{};ここでint aは非型テンプレート仮引数です。

2.2、非型仮引数はテンプレート定義の内部では定数値です。つまり、非型仮引数はテンプレートの内部では定数です。

    2.3、 非型テンプレートの仮引数は、整数型、ポインタ、参照のみです。、double、String、String **のような型は許可されません。ただし、double &,double *,オブジェクトの参照またはポインタは正しいです。

    2.4、 非型テンプレート仮引数に渡す実引数は定数式でなければなりません。つまり、コンパイル時に結果を計算できなければなりません。

2.5、注意:ローカルオブジェクト、ローカル変数、ローカルオブジェクトのアドレス、ローカル変数のアドレスはすべて定数式ではないため、非型テンプレート仮引数の実引数として使用できません。グローバルポインタ型、グローバル変数、グローバルオブジェクトも定数式ではないため、非型テンプレート仮引数の実引数として使用できません。

    2.6、 ; A<b> m; すなわち配列からポインタへの変換です。。

    2.7 、sizeof式の結果が定数式である場合も、非型テンプレート仮引数の実引数として使用できます。

2.8、テンプレートの仮引数が整数型の場合、そのテンプレートを呼び出す際の実引数は整数型でなければならず、かつコンパイル期間中に定数である必要があります。例えばtemplate <class T, int a> class A{};もしint b があり、このとき A<int, b> m;はエラーになります。なぜならbb は定数ではないからです。もしconst int b の場合、A<int, b> m; は正しいです。なぜならこのときbb は定数だからです。

    2.9 、非型仮引数は通常、関数テンプレート内では使用すべきではありません。、例えば関数テンプレートがあると、template<class T, int a> void h(T b){}これを使用してh(2){'translated_text': '{'translated_text': '呼び出すと、非型仮引数 a に対して実引数を推論できないというエラーが発生します。このようなテンプレート関数は、明示的テンプレート実引数を使用して解決できます。例えば、h<int, 3>(2) のようにすると、非型仮引数 a が整数 3 に設定されます。明示的テンプレート実引数については後で説明します。'}'}

2.10、非型テンプレート仮引数と実引数の間で許可される変換
1、配列からポインタへ、関数からポインタへの変換が許可されます。例:template <int *a> class A{}; int b
2、const修飾子の変換。例:template<const int *a> class A{}; int b; A<&b> m; すなわち int * から const int * への変換です。
3、昇格変換。例:template<int a> class A{}; const short b=2; A<b> m; すなわち short から int への昇格変換です。
4、整数値変換。例:template<unsigned int a> class A{}; A<3> m; すなわち int から unsigned int への変換です。
5、通常の変換。

非型仮引数のデモ例1:

  ユーザー自身がスタックのサイズを指定し、スタックに関する操作を実装します。

TemplateDemo.h

#ifndef TEMPLATE_DEMO_HXX #define TEMPLATE_DEMO_HXX template<class T,int MAXSIZE> class Stack{// MAXSIZEはユーザーがオブジェクト作成時に自ら設定する private: T elems[MAXSIZE]; // 要素を格納する配列 int numElems; // 要素の現在の総数 public: Stack(); // コンストラクタ void push(T const&); // 要素をプッシュする void pop(); // 要素をポップする T top() const; // スタックの先頭要素を返す bool empty() const{ // スタックが空かどうかを返す return numElems == 0; } bool full() const{ // スタックが満杯かどうかを返す return numElems == MAXSIZE; } }; template <class T,int MAXSIZE> Stack<T,MAXSIZE>::Stack():numElems(0){ // 初期状態ではスタックは要素を含まない // 何も行わない } template <class T,int MAXSIZE> void Stack<T, MAXSIZE>::push(T const& elem){ if(numElems == MAXSIZE){ throw std::out_of_range("Stack<>::push(): stack is full"); } elems[numElems] = elem; // 要素を追加する ++numElems; // 要素の数を増やす } template<class T,int MAXSIZE> void Stack<T,MAXSIZE>::pop(){ if (numElems <= 0) { throw std::out_of_range("Stack<>::pop(): empty stack"); } --numElems; // 要素の数を減らす } template <class T,int MAXSIZE> T Stack<T,MAXSIZE>::top()const{ if (numElems <= 0) { throw std::out_of_range("Stack<>::top(): empty stack"); } return elems[numElems-1]; // 最後の要素を返す } #endif

TemplateDemo.cpp

#include<iostream.h> #include <iostream> #include <string> #include <cstdlib> #include "TemplateDemo.h" int main(){ try { Stack<int,20> int20Stack; // 20個のint要素を格納できるスタック Stack<int,40> int40Stack; // 40個のint要素を格納できるスタック Stack<std::string,40> stringStack; // 40個のstring要素を格納できるスタック // 20個のint要素を格納できるスタックを使用する int20Stack.push(7); std::cout << int20Stack.top() << std::endl; //7 int20Stack.pop(); // 40個のstringを格納できるスタックを使用する stringStack.push("hello"); std::cout << stringStack.top() << std::endl; //hello stringStack.pop(); stringStack.pop(); //Exception: Stack<>::pop<>: empty stack return 0; } catch (std::exception const& ex) { std::cerr << "Exception: " << ex.what() << std::endl; return EXIT_FAILURE; // プログラムを終了し、ERRORマークを付ける } }

非型仮引数のデモ例2:

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

実行結果:-1 

TemplateDemo01.cpp

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

実行結果: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; }

非型仮引数のデモ例3:

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;// テンプレート実引数が暗黙的にdoubleに推論される cout<<max<double>(2.1,2.2)<<endl;// テンプレート引数を明示的に指定します。 cout<<max<int>(2.1,2.2)<<endl;// 明示的に指定されたテンプレート引数は、関数の引数を直接intに変換します。 }

実行結果:

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

この文では警告が発生します:

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

四、クラステンプレートのデフォルトテンプレート型仮引数

1、クラステンプレートの型仮引数にはデフォルト値を指定できますが、関数テンプレートの型仮引数にはデフォルト値を指定できません。関数テンプレートとクラステンプレートはどちらも、テンプレートの非型仮引数にデフォルト値を指定できます。

2、クラステンプレートの型仮引数のデフォルト値の形式は次のとおりです:template<class T1, class T2=int> class A{};2番目のテンプレート型仮引数にT2提供しますint型のデフォルト値を。

3、クラステンプレートの型仮引数のデフォルト値は関数のデフォルト引数と同じです。複数の型仮引数がある場合、最初の仮引数にデフォルト値を設定した後のすべてのテンプレート仮引数にもデフォルト値を設定する必要があります。例えばtemplate<class T1=int, class T2>class A{};これはエラーです。なぜならT1デフォルト値を指定したが、T2設定されていないからです。

4、クラステンプレートの外部でクラスのメンバーを定義する場合templatetemplate の後の仮引数リストではデフォルトの仮引数型を省略する必要があります。例えばtemplate<class  T1, class T2=int> class A{public: void h();}; 定義方法は次のとおりです。template<class T1,class T2> void A<T1,T2>::h(){}。

クラステンプレートの型仮引数を定義します:

デモ実例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; }

実行結果:5

クラステンプレートのデフォルトテンプレート型仮引数の例1:

TemplateDemo03.h

#ifndef TEMPLATE_DEMO_03 #define TEMPLATE_DEMO_03 // デフォルト型仮引数を持つクラステンプレートを定義します。ここではT2をデフォルトでint型に設定します。 template<class T1,class T2=int> class CeilDemo{ public: int ceil(T1,T2); }; // クラステンプレートの外部でクラスのメンバーを定義する場合、template の後の仮引数リストではデフォルトの仮引数型を省略する必要があります。 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; }

実行結果:2 

クラステンプレートの外部でクラスのメンバーを定義する場合、template の後の仮引数リストではデフォルトの仮引数型を省略する必要があります。省略しない場合、コンパイルエラーにはならず、警告が発生します:

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

原作者:クラステンプレートの型仮引数のデフォルト値は関数のデフォルト引数と同じです。複数の型仮引数がある場合、最初の仮引数にデフォルト値を設定した後のすべてのテンプレート仮引数にもデフォルト値を設定する必要があります。例えばtemplate<class T1=int, class T2>class A{};これはエラーです。なぜならT1デフォルト値を指定したが、T2設定されていないからです。

クラステンプレートのデフォルトテンプレート型仮引数の例2:

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

実行結果: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; }

コンパイルエラー:

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

上の例からわかるように、T2とT3をdouble型に定義しようとするとエラーが発生します(T1はデフォルトでint型に定義されています)。では、著者の言うとおりにT2とT3もデフォルト値のdoubleに設定した場合、エラーは発生しないのでしょうか?次の例を見てください:

クラステンプレートのデフォルトテンプレート型仮引数の例4:

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

コンパイルエラー:

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

結果からわかるように、前の例と同じエラーです。この実例から次のようにまとめられます:クラステンプレートに複数の型仮引数がある場合、型仮引数のデフォルト値を使用するなら、できるだけパラメータリストの末尾に配置し、デフォルトのパラメータ型は同じでなければなりません。最初の仮引数にデフォルト値を設定した場合、その後のすべてのテンプレート仮引数にも最初の仮引数と同じ型のデフォルト値を設定する必要があります。(声明:私自身もC++を始めたばかりで、以上は私が実例のデモを通して原作者に提示したいくつかの疑問にすぎません。私の例には不十分な点があるかもしれません。どうか達人の方々にご教示いただき、このブログを共に改善し、私のような初心者に学習の場を提供していただければ幸いです!)

次に、「関数テンプレートの型仮引数にデフォルト値を指定できない」ことを検証します:

クラステンプレートのデフォルトテンプレート型仮引数の例5:

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

コンパイルエラー:

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

変更後の 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; }

実行結果:261.1 

    

原作者のデモ例は次のとおりです:

クラステンプレート非型仮引数の例

// テンプレートの宣言または定義は、グローバル、名前空間、またはクラスのスコープ内でのみ行うことができます。つまり、ローカルスコープや関数内では行えません。例えば、main関数内でテンプレートを宣言または定義することはできません。 // クラステンプレートの定義 template<class T>class A{public:T g(T a, T b); A();}; // 1つのクラステンプレート型仮引数Tを持つクラスAを定義する template<class T1,class T2>class B{public:void g();}; // 2つのクラステンプレート型仮引数T1,T2を持つクラスBを定義する // クラステンプレートのデフォルト型仮引数を定義する。デフォルト型仮引数は関数テンプレートには適していません。 template<class T1,class T2=int> class D{public: voidg();}; // デフォルト型仮引数を持つクラステンプレートを定義します。ここではT2をデフォルトでint型に設定します。 //template<class T1=int, class T2>class E{}; // エラー。T1にデフォルト型仮引数を設定した場合、T1より後ろのすべての仮引数にデフォルト値を設定する必要があります。 // 以下は非型仮引数の定義です // 非型仮引数は整数型、ポインタ、参照のみ可能です。double、String、String ** のような型は許可されません。ただし、double &、double * オブジェクトの参照またはポインタは正しいです。template<class T1,int a> class Ci{public:void g();}; // テンプレートの非型仮引数を定義する。仮引数は整数型です template<class T1,int &a>class Cip{public:void g();}; template<class T1,A<int>* m> class Cc{public:void g();}; // テンプレートの非型仮引数を定義する。仮引数はint型のクラスAのオブジェクトへのポインタです。 template<class T1,double*a>class Cd{public:void g();}; // テンプレートの非型仮引数を定義する。仮引数はdouble型の参照です。 class E{}; template<class T1,E &m> class Ce{}; // 非型テンプレート仮引数がオブジェクトの参照です。 // 以下の非型仮引数の宣言はエラーです。 //template<class T1,A m>class Cc{}; // エラー。オブジェクトは非型仮引数にできません。非型テンプレート仮引数の型は、オブジェクトの参照またはポインタのみ可能です。 //template<class T1,double a>class Cc{}; // エラー。非型テンプレートの仮引数はdouble型にできませんが、doubleの参照は可能です。 //template<class T1,A<int> m>class Cc{}; // エラー。非型テンプレートの仮引数はオブジェクトにできません。オブジェクトの参照またはポインタでなければなりません。この規則はテンプレート型仮引数にも例外ではありません。// クラステンプレートの外部で各種クラスメンバーを定義する方法、 //typeid(変数名).name()の役割は変数名の型を抽出することです。例えばint aの場合、cout<<typeid(a).name()はintを出力します。 template<class T> A<T>::A(){cout<<"class A goucao"<<typeid(T).name()<<endl;} //クラステンプレートの外部でクラスのコンストラクタを定義する方法 template<class T> T A<T>::g(T a,T b){cout<<"class A g(T a,T b)"<<endl;} //クラステンプレートの外部でクラステンプレートのメンバーを定義します template<class T1,class T2> voidB<T1,T2>::g(){cout<<"class g f()"<<typeid(T1).name()<<typeid(T2).name()<<endl;} //クラスの外部でクラスのメンバーを定義する場合、templateの後のテンプレート仮引数は定義するクラスのテンプレート仮引数と一致する必要があります。 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;} //クラス外部でクラスのメンバーを定義する場合、templateの後のテンプレート仮引数は定義するクラスのテンプレート仮引数と一致する必要があります。 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;} //デフォルト型仮引数を持つテンプレートクラスで、クラスの外部でメンバーを定義する方法。 //クラス外部でクラスのメンバーを定義する場合、templateの仮引数リストのデフォルト値は省略する必要があります。 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;} //エラー、クラステンプレートの外部でデフォルト型を持つ仮引数を定義する場合仮引数の定義時、templatetemplateの仮引数リストではデフォルト値を省略する必要があります。//いくつかのグローバル変数を定義します。 int e=2; doubleed=2.2; double*pe=&ed; A<int> mw; A<int> *pec=&mw; E me; //main関数の始まり int main() { // template<class T>void h(){} //エラー、テンプレートの宣言または定義はグローバル、名前空間、またはクラススコープ内でのみ行えます。つまり、ローカルスコープや関数内では行えません。 //A<2> m; //エラー、クラステンプレートには実引数推定の問題は存在せず、クラステンプレートは山括弧内でその型を明示的に指定する必要があります。 //クラステンプレート呼び出しのインスタンス A<int> ma; //"class A goucao int"と出力し、int型のクラステンプレートAのオブジェクトmaを作成します。 B<int,int> mb; mb.g(); //"class B g() int int"と出力し、クラステンプレートBのオブジェクトmbを作成し、型仮引数T1とT2をintに設定します。 //非型仮引数の呼び出し //非型テンプレート仮引数の実引数は定数式でなければなりません。つまり、コンパイル時に結果を計算できる必要があります。任意のローカルオブジェクト、ローカル変数、ローカルオブジェクトのアドレス、ローカル変数のアドレスは定数式ではなく、非型テンプレート仮引数の実引数として使用できません。グローバルポインタ型、グローバル変数、グローバルオブジェクトも定数式ではなく、非型テンプレート仮引数の実引数として使用できません。//グローバル変数のアドレスまたは参照、グローバルオブジェクトのアドレスまたは参照、const型変数は定数式であり、非型テンプレート仮引数の実引数として使用できます。 //整数int型の非型仮引数を呼び出す方法は、クラス名がCiで、宣言形式はtemplate<class T1,int a> class Ciです。 Ci<int,3>//正しい、数値Rはint型の定数であり、"class Ci g() int"と出力します。 const int a2=3; Ci<int,a2> mci1; mci1.g(); //正しい、a2はここではconst型の定数であるためです。"class Ci g() int"と出力します。 //Ci<int,a> mci; //エラー、int型変数aはローカル変数であり、定数式ではありません。 //Ci<int,e> mci; //エラー、グローバルint型変数eも定数式ではありません。 //int&型の非型仮引数を呼び出す方法は、クラス名がCipで、宣言形式はtemplate<class T1,int &a>class Cipです。 Cip<int,e> mcip; //正しい、グローバル変数への参照またはアドレスは定数式です。 //Cip<int,a> mcip1; //エラー、ローカル変数の参照またはアドレスは定数式ではありません。 //double*型の非型仮引数を呼び出す方法は、クラス名がCdで、宣言形式はtemplate<class T1,double *a>class Cdです。 Cd<int,&ed> mcd; //正しい、グローバル変数の参照またはアドレスは定数式です。 //Cd<int,pe> mcd1; //エラー、グローバル変数のポインタは定数式ではありません。 //double dd=3.3; //エラー、ローカル変数のアドレスは定数式ではないため、非型仮引数の実引数として使用できません。 //Cd<int,&e> mcd; //エラー、非型仮引数は一部の変換を許可しますが、この変換は実現できません。 //テンプレート型仮引数オブジェクトA<int>*のメソッドを呼び出す方法は、クラス名がCcで、宣言形式はtemplate<class T1,A<int>* m> class Ccです。 Cc<int,&mw> mcc; mcc.g(); //正しい、グローバルオブジェクトのアドレスまたは参照は定数式です。 //Cc<int,&ma> mcc; //エラー、ローカル変数のアドレスまたは参照は定数式ではありません。 //Cc<int,pec> mcc2; //エラー、グローバルオブジェクトのポインタは定数式ではありません。 //非型仮引数E&オブジェクトの参照を呼び出す方法は、クラス名がCeです。宣言形式はtemplate<class T1,E &m> class Ceです。 E me1; //Ce<int,me1> mce1; //エラー、ローカルオブジェクトは定数式ではありません。 Ce<int,me> mce; //正しい、グローバルオブジェクトのポインタまたは参照は定数式です。 //非型仮引数の変換例、クラス名はCi //非型仮引数は、配列からポインタへの変換、関数からポインタへの変換、const修飾子の変換、昇格変換、整数変換、通常の変換を許可します。 const short s=3; Ci<int,s> mci4†//正しい、short型とint型は完全には一致しませんが、ここではshort型をint型に変換できます。

原文アドレス:

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

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