メンバテンプレート
テンプレートの宣言 (クラス、関数、および変数 (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 |