C++ Academy · レッスン

enable_ifパターン

従来の方法でテンプレートを制約します

レッスン 4/413 ステップ

「enable_ifパターン」はCoddyKit上の無料C++ Academyレッスンです。 これはレッスン4/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはC++ Academy学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 C++ Academyコースには全4レッスンが含まれています。

enable_ifとは

std::enable_ifは、typeメンバーを条件付きで定義する小さな型特性です。条件がtrueの場合は型が存在し、falseの場合はSFINAEによって周囲のテンプレートが除外されます。

  • <type_traits>に定義されています。
  • テンプレートに制約を付ける従来からのツールです。
#include <iostream>
#include <type_traits>

template <typename T>
typename std::enable_if<std::is_integral<T>::value, T>::type
doubleIt(T v) { return v * 2; }

int main() {
    std::cout << doubleIt(21) << '\n';
    return 0;
}

仕組み

enable_if<Cond, T>::typeは、Condがtrueの場合にだけTになります。falseの場合はtypeメンバーが存在しないため、置換に失敗します。

#include <iostream>
#include <type_traits>

int main() {
    std::cout << std::boolalpha;
    std::cout << std::is_same<std::enable_if<true, int>::type, int>::value << '\n';
    return 0;
}

戻り値の型でenable_ifを使う

最も一般的な配置場所は戻り値の型です。条件を満たす場合にだけ関数が存在します。

#include <iostream>
#include <type_traits>

template <typename T>
typename std::enable_if<std::is_floating_point<T>::value, T>::type
half(T v) { return v / 2; }

int main() {
    std::cout << half(5.0) << '\n';
    return 0;
}

テンプレートパラメータとしてenable_ifを使う

別のイディオムでは、デフォルト引数を持つテンプレートパラメータにenable_ifを配置し、戻り値の型をすっきりさせます。

#include <iostream>
#include <type_traits>

template <typename T, typename = typename std::enable_if<std::is_integral<T>::value>::type>
T triple(T v) { return v * 3; }

int main() {
    std::cout << triple(4) << '\n';
    return 0;
}

enable_if_tショートカット

C++14ではエイリアスstd::enable_if_t<Cond, T>が追加され、typename ...::typeという冗長な記述を省略できるようになりました。

#include <iostream>
#include <type_traits>

template <typename T>
std::enable_if_t<std::is_integral<T>::value, T>
negate(T v) { return -v; }

int main() {
    std::cout << negate(8) << '\n';
    return 0;
}

2つのオーバーロードを組み合わせる

相補的な条件を指定して、どの型に対しても必ず一方だけが有効になり、曖昧さが生じないようにします。

#include <iostream>
#include <type_traits>

template <typename T>
std::enable_if_t<std::is_integral<T>::value, const char*>
category() { return "integer"; }

template <typename T>
std::enable_if_t<!std::is_integral<T>::value, const char*>
category() { return "non-integer"; }

int main() {
    std::cout << category<int>() << ' ' << category<double>() << '\n';
    return 0;
}

条件を組み合わせる

型特性の値に論理演算子を適用して、「整数型かつ符号付き」のような、より複雑な制約を表現します。

#include <iostream>
#include <type_traits>

template <typename T>
std::enable_if_t<std::is_integral<T>::value && std::is_signed<T>::value, T>
magnitude(T v) { return v < 0 ? -v : v; }

int main() {
    std::cout << magnitude(-12) << '\n';
    return 0;
}

コンストラクタに制約を付ける

コンストラクタのテンプレートパラメータにenable_ifを配置し、互換性のある型に対してだけ使用できるようにします。

#include <iostream>
#include <type_traits>

struct Wrapper {
    int value;
    template <typename T, typename = std::enable_if_t<std::is_integral<T>::value>>
    Wrapper(T v) : value(static_cast<int>(v)) {}
};

int main() {
    Wrapper w(42);
    std::cout << w.value << '\n';
    return 0;
}

デフォルトテンプレート引数の落とし穴

デフォルト値を持つenable_ifテンプレートパラメータだけが異なる2つのオーバーロードは、再定義とみなされます。異なる形式にしてください(たとえば、一方の条件を戻り値の型に配置します)。

#include <iostream>
#include <type_traits>

template <typename T>
std::enable_if_t<std::is_integral<T>::value, void>
printKind() { std::cout << "int\n"; }

template <typename T>
std::enable_if_t<std::is_floating_point<T>::value, void>
printKind() { std::cout << "float\n"; }

int main() {
    printKind<int>();
    printKind<double>();
    return 0;
}

Conceptとの比較

C++20のconceptは、ほとんどのenable_ifの使用をより明快な構文に置き換えます。ただし、C++20より前のコードや一部の特殊なケースでは、enable_ifが今も不可欠です。

#include <iostream>
#include <type_traits>

template <typename T>
std::enable_if_t<std::is_arithmetic<T>::value, T>
square(T v) { return v * v; }

int main() {
    std::cout << square(6) << ' ' << square(1.5) << '\n';
    return 0;
}

完全な例

すべてを組み合わせて、整数と浮動小数点数を異なる方法でフォーマットする関数群を、コンパイル時に選択します。

#include <iostream>
#include <type_traits>

template <typename T>
std::enable_if_t<std::is_integral<T>::value, void>
format(T v) { std::cout << "int[" << v << "]\n"; }

template <typename T>
std::enable_if_t<std::is_floating_point<T>::value, void>
format(T v) { std::cout << "float(" << v << ")\n"; }

int main() {
    format(10);
    format(3.5);
    return 0;
}

クイックチェック

enable_ifを理解できているか確認しましょう。

まとめ

従来から使われているenable_ifのパターンを学びました。

  • 条件がtrueの場合にだけ::typeを定義し、SFINAEを有効にします
  • 戻り値の型またはデフォルト値を持つテンプレートパラメータに配置します
  • enable_if_tエイリアスを使い、論理演算子で型特性を組み合わせます
  • C++20ではconceptに取って代わられましたが、今も広く使われています

これでTemplate Specialization and SFINAEコースは完了です。

無料で開始

AI チューターと学ぶ C++ — 無料

ブラウザでリアルコードを書いて実行し、24/7 の AI チューターから瞬時にサポートを受け、ウェブまたはアプリで続きから学習できます。

コース
51
レッスン
203

よくある質問

「enable_ifパターン」レッスンは無料ですか?

はい。「enable_ifパターン」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、C++ Academyコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 C++ Academyコースには全4レッスンが含まれています。

「enable_ifパターン」で何を学びますか?

従来の方法でテンプレートを制約します ブラウザで直接実行するハンズオンコードでC++ Academyを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。

C++ Academyを始めるのに経験は必要ですか?

事前経験は必要ありません。CoddyKitのC++ Academyは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン4/4です。

「enable_ifパターン」レッスンにはどのくらい時間がかかりますか?

ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。

このC++ Academyレッスンでコードを書いて実行できますか?

はい。すべてのC++ Academyレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。

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

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