Member templates
テンプレート宣言 ( class , function , および variables (C++14以降) ) は、 member specification 内に現れることができます。これは、 local classes ではない任意のクラス、構造体、または共用体に適用されます。
#include <algorithm> #include <iostream> #include <string> #include <vector> 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::ranges::for_each(s, 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*> {}; // 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 を禁止 |