部分特殊化
テンプレートパターンを特殊化します
「部分特殊化」はCoddyKit上の無料C++ Academyレッスンです。 これはレッスン2/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはC++ Academy学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 C++ Academyコースには全4レッスンが含まれています。
部分特殊化とは
部分特殊化は、1つの特定の型ではなく、共通するパターンを持つ型の集合に対してテンプレートをカスタマイズします。
- クラステンプレートだけがサポートします。
- 一部のパラメータは汎用のままにします。
#include <iostream>
template <typename T>
struct Traits { static const char* kind() { return "value"; } };
template <typename T>
struct Traits<T*> { static const char* kind() { return "pointer"; } };
int main() {
std::cout << Traits<int>::kind() << '\n';
std::cout << Traits<int*>::kind() << '\n';
return 0;
}ポインタに一致させる
T*というパターンは任意のポインタ型に一致するため、すべてのポインタを1つの特殊化で処理できます。
#include <iostream>
template <typename T>
struct Deref { static T get(T v) { return v; } };
template <typename T>
struct Deref<T*> { static T get(T* p) { return *p; } };
int main() {
int x = 42;
std::cout << Deref<int>::get(7) << '\n';
std::cout << Deref<int*>::get(&x) << '\n';
return 0;
}関数は部分特殊化できない
部分特殊化をサポートするのは、クラステンプレート(および変数テンプレート)だけです。関数では、代わりにオーバーロードを使用します。
#include <iostream>
template <typename T>
void show(T) { std::cout << "value\n"; }
template <typename T>
void show(T*) { std::cout << "pointer\n"; }
int main() {
int x = 0;
show(5);
show(&x);
return 0;
}2つのパラメータのうち1つを特殊化する
複数の型パラメータがある場合、一方を固定してもう一方を汎用のままにすることで、型全体にまたがるパターンに一致させられます。
#include <iostream>
template <typename A, typename B>
struct Pair { static const char* kind() { return "mixed"; } };
template <typename A>
struct Pair<A, A> { static const char* kind() { return "same types"; } };
int main() {
std::cout << Pair<int, double>::kind() << '\n';
std::cout << Pair<int, int>::kind() << '\n';
return 0;
}配列に一致させる
要素の型とサイズを非型パラメータとして取り出すパターンを使って、配列型に一致させることができます。
#include <iostream>
template <typename T>
struct Info { static int size() { return 1; } };
template <typename T, int N>
struct Info<T[N]> { static int size() { return N; } };
int main() {
std::cout << Info<int>::size() << '\n';
std::cout << Info<int[5]>::size() << '\n';
return 0;
}constパターン
const Tをパターンとして一致させることで、特殊化によってconstを取り除いたり検出したりできます。
#include <iostream>
template <typename T>
struct IsConst { static const bool value = false; };
template <typename T>
struct IsConst<const T> { static const bool value = true; };
int main() {
std::cout << std::boolalpha;
std::cout << IsConst<int>::value << '\n';
std::cout << IsConst<const int>::value << '\n';
return 0;
}最も特殊なものが選ばれる
複数の特殊化が一致する可能性がある場合、コンパイラは最も特殊なもの、つまり最も具体的なパターンを選びます。
#include <iostream>
template <typename T>
struct K { static int v() { return 0; } };
template <typename T>
struct K<T*> { static int v() { return 1; } };
template <>
struct K<int*> { static int v() { return 2; } };
int main() {
std::cout << K<double*>::v() << ' ' << K<int*>::v() << '\n';
return 0;
}参照パターン
参照型にも一致させることができ、参照と単なる値を区別できます。
#include <iostream>
template <typename T>
struct Cat { static const char* k() { return "value"; } };
template <typename T>
struct Cat<T&> { static const char* k() { return "lvalue ref"; } };
int main() {
std::cout << Cat<int>::k() << '\n';
std::cout << Cat<int&>::k() << '\n';
return 0;
}型特性を構築する
部分特殊化は型特性の基盤です。ここでは自作のremove_pointerを示します。
#include <iostream>
#include <type_traits>
template <typename T>
struct RemovePtr { using type = T; };
template <typename T>
struct RemovePtr<T*> { using type = T; };
int main() {
std::cout << std::boolalpha;
std::cout << std::is_same<RemovePtr<int*>::type, int>::value << '\n';
return 0;
}パターンを組み合わせる
パターンを組み合わせることもできます。たとえばconstへのポインタに一致させて、何層にもなった型構造を処理できます。
#include <iostream>
template <typename T>
struct Desc { static const char* k() { return "plain"; } };
template <typename T>
struct Desc<const T*> { static const char* k() { return "ptr to const"; } };
int main() {
std::cout << Desc<int>::k() << '\n';
std::cout << Desc<const int*>::k() << '\n';
return 0;
}実用的なコンテナ型特性
部分特殊化を使って、コンテナのようなテンプレートの要素型を取り出します。
#include <iostream>
#include <type_traits>
template <typename C>
struct Element { using type = void; };
template <template <typename> class C, typename T>
struct Element<C<T>> { using type = T; };
template <typename T>
struct Box { T item; };
int main() {
std::cout << std::boolalpha;
std::cout << std::is_same<Element<Box<int>>::type, int>::value << '\n';
return 0;
}クイックチェック
部分特殊化を理解できているか確認しましょう。
まとめ
部分特殊化について学びました。
T*やconst Tのような型のパターンに対してクラステンプレートをカスタマイズします- 関数では部分特殊化ではなくオーバーロードを使用します
- 一致する中で最も特殊なバージョンが選ばれます
remove_pointerなどの型特性を支えています
次は、型が条件を満たす場合にだけ関数を有効にする仕組み、SFINAEについて学びます。
よくある質問
「部分特殊化」レッスンは無料ですか?
はい。「部分特殊化」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、C++ Academyコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 C++ Academyコースには全4レッスンが含まれています。
「部分特殊化」で何を学びますか?
テンプレートパターンを特殊化します ブラウザで直接実行するハンズオンコードでC++ Academyを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。
C++ Academyを始めるのに経験は必要ですか?
事前経験は必要ありません。CoddyKitのC++ Academyは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン2/4です。
「部分特殊化」レッスンにはどのくらい時間がかかりますか?
ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。
このC++ Academyレッスンでコードを書いて実行できますか?
はい。すべてのC++ Academyレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。