enable_ifパターン
従来の方法でテンプレートを制約します
「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フィードバックを取得できます。ローカル設定は不要です。