メンバテンプレート
テンプレートの宣言 (クラス、関数、および変数 (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*> { }; // OK template<class T> struct A::B<T&> { }; // OK
囲っているクラスの宣言もまたクラステンプレートである場合は、メンバテンプレートをクラス本体の外側で定義するとき、テンプレート仮引数のセットを2つ取ります (ひとつは囲っているクラス用で、もうひとつは自分用です)。
template<typename T1> struct string { // メンバテンプレート関数 template<typename T2> int compare(const T2&); // コンストラクタもテンプレートにできます template<typename T2> string(const std::basic_string<T2>& s) { /*...*/ } }; // string<T1>::compare<T2> のクラス外側の定義 template<typename T1> // 囲っているクラステンプレート用 template<typename T2> // メンバテンプレート用 int string<T1>::compare(const T2& s) { /* ... */ }
目次 |
[編集] メンバ関数テンプレート
デストラクタおよびコピーコンストラクタはテンプレートにできません。 テンプレートコンストラクタをコピーコンストラクタの型シグネチャで実体化できるように宣言しても、代わりに暗黙に宣言されたコピーコンストラクタが使用されます。
メンバ関数テンプレートは仮想にできません。 また、派生クラスのメンバ関数テンプレートは基底クラスの仮想メンバ関数をオーバーライドできません。
class Base { virtual void 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::operator char*() {return nullptr;} // 明示的実体化 template A::operator void*(); int main() { A a; int* ip = a.operator int*(); // 明示的な A::operator int*() の呼び出し }
オーバーロード解決において、変換関数テンプレートの特殊化は名前探索によって発見されません。 代わりに、すべての可視な変換関数テンプレートが考慮され、テンプレートの実引数推定 (変換関数テンプレートのための特別なルールがあります) によって生成されるすべての特殊化が、名前探索によって発見されたかのように使用されます。
派生クラスの using 宣言は基底クラスのテンプレート変換関数の特殊化を参照できません。
ユーザ定義変換関数テンプレートは戻り値の型を推定できません。 struct S { operator auto() const { return 10; } // OK template<class T> operator auto() const { return 42; } // エラー }; |
(C++14以上) |
メンバ変数テンプレート変数テンプレートの宣言はクラススコープで行うこともできます。 この場合は静的データメンバテンプレートを宣言します。 詳細については変数テンプレートを参照してください。 |
(C++14以上) |
[編集] 欠陥報告
以下の動作変更欠陥報告は以前に発行された C++ 標準に遡って適用されました。
DR | 適用先 | 発行時の動作 | 正しい動作 |
---|---|---|---|
CWG 1878 | C++14 | operator auto was technically allowed | operator auto forbidden |