テンプレート宣言 (
class
,
function
, および
variables
(C++14以降)
) は、
member specification
内に現れることができます。これは、
local classes
ではない任意のクラス、構造体、または共用体に適用されます。
メンバーテンプレートの部分特殊化は、クラススコープとそれを囲む名前空間スコープの両方で宣言できます。明示的特殊化は、プライマリテンプレートが宣言可能な任意のスコープで宣言できます。
struct A
{
template<class T> struct B; // プライマリメンバーテンプレート
template<class T> struct B<T*> {}; // OK: 部分特殊化
// template<> struct B<int*> {}; // CWG 727経由でOK: 完全特殊化
};
template<> struct A::B<int*> {}; // OK
template<class T> struct A::B<T&> {}; // OK
外側のクラス宣言がクラステンプレートである場合、メンバーテンプレートをクラス本体の外側で定義するとき、2組のテンプレートパラメータを取ります:1つは外側のクラスのため、もう1つは自身のためのものです:
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) { /* ... */ }
メンバー関数テンプレート
Destructor
と
copy constructor
はテンプレートにできません。copy constructorの型シグネチャでインスタンス化可能なテンプレートコンストラクタが宣言されている場合、
implicitly-declared copy constructor
が代わりに使用されます。
メンバー関数テンプレートは仮想にできず、派生クラスのメンバー関数テンプレートは基底クラスの仮想メンバー関数をオーバーライドできません。
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>(char) を呼び出す
ac.f(1); // 非テンプレート関数 A<char>::f(int) を呼び出す
ac.f<>(1); // テンプレート関数 A<char>::f<int>(int) を呼び出す
}
メンバー関数テンプレートのクラス外定義は、クラス内の宣言と
等価
でなければなりません(等価性の定義については
function template overloading
を参照)。そうでない場合、オーバーロードとして扱われます。
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内部のどの宣言とも等価ではない
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*() の明示的呼び出し
}
overload resolution
の間、
name lookup
によって変換関数テンプレートの特殊化は見つかりません。代わりに、可視の変換関数テンプレートがすべて考慮され、
template argument deduction
(変換関数テンプレートには特別なルールがあります)によって生成されるすべての特殊化が、name lookup によって見つかったかのように使用されます。
派生クラス内のusing宣言は、基底クラスからのテンプレート変換関数の特殊化を参照できません。
|
ユーザー定義変換関数テンプレートは推論された戻り値の型を持つことはできません:
struct S
{
operator auto() const { return 10; } // OK
template<class T> operator auto() const { return 42; } // error
};
|
(C++14以降)
|
メンバ変数テンプレート
変数テンプレートの宣言はクラススコープで現れることができ、その場合は静的データメンバーテンプレートを宣言します。詳細については
variable templates
を参照してください。
|
(C++14以降)
|
不具合報告
以下の動作変更の欠陥報告書は、以前に公開されたC++規格に対して遡及的に適用されました。
|
DR
|
Applied to
|
Behavior as published
|
Correct behavior
|
|
CWG 1878
|
C++14
|
operator auto が技術的に許可されていた
|
operator auto を禁止
|