特殊化は、特定のテンプレート引数リストに対する定義を提供します。クラステンプレートと変数テンプレートの場合は、より具体的な引数パターンに対する定義も提供します。型に基づくディスパッチが適切なインターフェイスである場合にのみ特殊化を選択してください。関数固有のディスパッチには通常のオーバーロードを、振る舞いに基づく適格性の判定にはコンセプトを使用します。
完全特殊化 #
完全特殊化はすべての引数を指定し、template <> で始まります。関数テンプレート、クラステンプレート、変数テンプレートは完全特殊化できます。
template <class T>
struct formatter {
static const char* name() { return "other"; }
};
template <>
struct formatter<int> {
static const char* name() { return "int"; }
};完全特殊化は独立した実体です。プライマリーテンプレートのメンバー、定義、デフォルトテンプレート引数を継承しないため、必要なものをすべて定義してください。その特殊化を使用するすべての翻訳単位において、特殊化が暗黙的にインスタンス化される最初の使用より前に、完全特殊化を宣言してください。
部分特殊化 #
クラステンプレートと変数テンプレートは部分特殊化できます。部分特殊化はパターンに一致し、コンパイラーは半順序付けによって、一致するパターンのうち最も特殊化されたものを選択します。
#include <type_traits>
template <class T>
struct is_pointer_like : std::false_type {};
template <class T>
struct is_pointer_like<T*> : std::true_type {};
template <class T>
inline constexpr bool is_pointer_like_v = is_pointer_like<T>::value;部分特殊化にはプライマリーテンプレートの宣言が必要です。その引数リストはプライマリーよりも特殊化されている必要があり、言語規則のもとで引き続き推論可能でなければなりません。一致する部分特殊化同士を比較できない場合、その使用は曖昧となり、コンパイルに失敗します。
関数テンプレートは部分特殊化できません。代わりに、オーバーロードした関数テンプレートを追加するか、制約を適用するか、特殊化したクラスまたは変数のトレイトを介してディスパッチしてください。
順序とライブラリの境界 #
呼び出し側が暗黙的にインスタンス化する可能性がある場合、プライマリーと適用可能な特殊化の宣言をヘッダーに配置してください。特殊化を 1 つの翻訳単位だけで定義するのは、その宣言が暗黙的なインスタンス化が起こり得るすべての箇所より前で可視となり、かつリンケージがそれに応じて構成されている場合に限ります。
トレイトを特殊化すると、明確なディスパッチ境界を作成できます。
struct LocalPacket {};
template <class T>
inline constexpr bool uses_fast_path_v = false;
template <>
inline constexpr bool uses_fast_path_v<LocalPacket> = true;プログラムが LocalPacket とトレイトを所有しているため、契約は明示的です。アクセス制御を回避したり、無関係な意味を変更したりするために特殊化を使用しないでください。
標準ライブラリは事情が異なります。ライブラリコンポーネントに別段の規定がない限り、プログラムが namespace std に標準ライブラリテンプレートの特殊化を追加できるのは、その宣言がプログラム定義型を少なくとも 1 つ含み、特殊化が元のテンプレートの要件を満たす場合に限ります。特定のコンポーネントでは、特殊化が禁止されている場合や追加要件が課される場合があります。通常の言語規則のもとで部分特殊化できるのはクラステンプレートと変数テンプレートだけであり、関数テンプレートは部分特殊化できません。std にオーバーロード、プライマリーテンプレート、または無関係な宣言を追加する許可があると推測しないでください。std::hash と std::formatter の特殊化、引数依存の swap、ranges のカスタマイズ、プロジェクト所有のトレイトなど、文書化されたカスタマイズポイントを優先してください。
コメント
GitHub で閲覧・コメントする(投稿にはログインが必要です)