メンバテンプレート
テンプレートの宣言 (クラス、関数、および変数(C++14以上)) は、ローカルクラスでない任意のクラス、構造体、または共用体の中でも行うことができます。
#include <iostream>#include <vector>#include <algorithm> struct Printer {// 総称ファンクタstd::ostream& os; Printer(std::ostream& os): os(os){}template<typename T>void operator()(const T& obj){ os << obj <<' ';}// メンバテンプレート}; int main(){std::vector<int> v ={1,2,3};std::for_each(v.begin(), v.end(), Printer(std::cout));std::string s ="abc";std::for_each(s.begin(), s.end(), Printer(std::cout));}
出力:
1 2 3 a b c
メンバテンプレートの部分特殊化はクラススコープと囲っている名前空間スコープのどちらでもできますが、明示的特殊化は囲っている名前空間スコープでのみできます。
struct A {template<class T>struct B;// プライマリメンバテンプレートtemplate<class T>struct B<T*>{};// OK、部分特殊化// template<> struct B<int*> { }; // エラー、完全特殊化};template<>struct A::B<int*>{};// OKtemplate<class T>struct A::B<T&>{};// OK
囲っているクラスの宣言もまたクラステンプレートである場合は、メンバテンプレートをクラス本体の外側で定義するとき、テンプレート仮引数のセットを2つ取ります (ひとつは囲っているクラス用で、もうひとつは自分用です)。
template<typename T1>struct string {// メンバテンプレート関数template<typename T2>int compare(const T2&);// コンストラクタもテンプレートにできますtemplate<typename T2> string(conststd::basic_string<T2>& s){/*...*/}};// string<T1>::compare<T2> のクラス外側の定義template<typename T1>// 囲っているクラステンプレート用template<typename T2>// メンバテンプレート用int string<T1>::compare(const T2& s){/* ... */}
目次 |
[編集]メンバ関数テンプレート
デストラクタおよびコピーコンストラクタはテンプレートにできません。 テンプレートコンストラクタをコピーコンストラクタの型シグネチャで実体化できるように宣言しても、代わりに暗黙に宣言されたコピーコンストラクタが使用されます。
メンバ関数テンプレートは仮想にできません。 また、派生クラスのメンバ関数テンプレートは基底クラスの仮想メンバ関数をオーバーライドできません。
class Base {virtualvoid f(int);};struct Derived : Base {// このメンバテンプレートは Base::f をオーバーライドしません。template<class T>void f(T); // 非テンプレートメンバのオーバーライドがテンプレートを呼ぶことはできます。void f(int i) override { f<>(i);}};
同じ名前を持つ非テンプレートメンバ関数とテンプレートメンバ関数を宣言しても構いません。 衝突した場合 (何らかのテンプレート特殊化が非テンプレート関数のシグネチャと正確に一致したとき) は、明示的なテンプレート引数リストが与えられない限り、非テンプレートメンバを参照します。
template<typename T>struct A {void f(int);// 非テンプレートメンバ template<typename T2>void f(T2);// メンバテンプレート}; // メンバテンプレートの定義template<typename T>template<typename T2>void A<T>::f(T2){// 何らかのコード} int main(){ A<char> ac; ac.f('c');// テンプレート関数 A<char>::f<char>(int) を呼びます。 ac.f(1);// 非テンプレート関数 A<char>::f(int) を呼びます。 ac.f<>(1);// テンプレート関数 A<char>::f<int>(int) を呼びます。}
メンバ関数テンプレートのクラス外側の定義は、クラス内側の宣言と同等でなければなりません (同等の定義については関数テンプレートのオーバーロードを参照してください)。 そうでなければ、それはオーバーロードであるとみなされます。
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、同等な宣言です。template<class V> V X::good(V n){return n;} // エラー、 X の内側のどの宣言とも同等でありません。 Error: not equivalent to any of the declarations inside X template<class T> T X::bad(typename identity<T>::type n){return n;}
[編集]変換関数テンプレート
ユーザ定義変換関数はテンプレートにできます。
struct A {template<typename T> operator T*();// 任意の型のポインタへの変換}; // クラス外側の定義template<typename T> A::operator T*(){return nullptr;} // char* に対する明示的特殊化template<> A::operatorchar*(){return nullptr;} // 明示的実体化template A::operatorvoid*(); int main(){ A a;int* ip = a.operatorint*();// 明示的な A::operator int*() の呼び出し}
オーバーロード解決において、変換関数テンプレートの特殊化は名前探索によって発見されません。 代わりに、すべての可視な変換関数テンプレートが考慮され、テンプレートの実引数推定 (変換関数テンプレートのための特別なルールがあります) によって生成されるすべての特殊化が、名前探索によって発見されたかのように使用されます。
派生クラスの using 宣言は基底クラスのテンプレート変換関数の特殊化を参照できません。
ユーザ定義変換関数テンプレートは戻り値の型を推定できません。 struct S { operator auto()const{return10;}// OKtemplate<class T> operator auto()const{return42;}// エラー}; | (C++14以上) |
メンバ変数テンプレート変数テンプレートの宣言はクラススコープで行うこともできます。 この場合は静的データメンバテンプレートを宣言します。 詳細については変数テンプレートを参照してください。 | (C++14以上) |
[編集]欠陥報告
以下の動作変更欠陥報告は以前に発行された C++ 標準に遡って適用されました。
DR | 適用先 | 発行時の動作 | 正しい動作 |
---|---|---|---|
CWG 1878 | C++14 | operator auto was technically allowed | operator auto forbidden |