0Pricing
C++ Academy · レッスン

部分特殊化

テンプレートパターンを特殊化します

「部分特殊化」は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フィードバックを取得できます。ローカル設定は不要です。

このコースのすべてのレッスン

  1. 完全特殊化
  2. 部分特殊化
  3. SFINAE
  4. enable_ifパターン
← C++ Academyに戻る