enable_if クラス

SFINAE オーバーロード解決用のタイプのインスタンスを条件によって作成する 入れ子の typedef enable_if<Condition,Type>::typeType のシノニムで、Conditiontrue の場合のみ存在します。

構文

template <bool B, class T = void>
struct enable_if;

パラメーター

B
結果の型の存在を定義する値。

T
Btrue の場合にインスタンスを作成するタイプ。

解説

Btrue の場合、enable_if<B, T> は "type" という名前の入れ子の typedef を持ちます。これは T のシノニムです。

Bfalse の場合、enable_if<B, T> は "type" という名前の入れ子の typedef を持ちません。

このエイリアス テンプレートが提供されています。

template <bool B, class T = void>
using enable_if_t = typename enable_if<B,T>::type;

C++ では、テンプレート パラメーターの置き換えの失敗は、それ自体はエラーではありません。これは SFINAE (substitution failure is not an error: 置き換えの失敗はエラーではない) と呼ばれます。 一般的に、enable_if はオーバーロード解決から候補を外す (つまりオーバーロード セットを除外する) ために使用します。一方を優先させて他方の定義が拒否されることもあります。 これは、SFINAE の動作に適合します。 SFINAE の詳細については、Wikipedia の「Substitution failure isn't an error」を参照してください。

次に、4 つのシナリオの例を示します。

  • シナリオ 1: 関数の戻り値の型をラッピングする
    template <your_stuff>
typename enable_if<your_condition, your_return_type>::type
    yourfunction(args) {// ...
}
// The alias template makes it more concise:
    template <your_stuff>
enable_if_t<your_condition, your_return_type>
yourfunction(args) {// ...
}
  • シナリオ 2: 既定の引数を持つ関数パラメーターを追加する
    template <your_stuff>
your_return_type_if_present
    yourfunction(args, enable_if_t<your condition, FOO> = BAR) {// ...
}
  • シナリオ 3: 既定の引数を持つテンプレート パラメーターを追加する
    template <your_stuff, typename Dummy = enable_if_t<your_condition>>
rest_of_function_declaration_goes_here
  • シナリオ 4: 関数がテンプレート化されていない引数を持っている場合、その型をラッピングできる
    template <typename T>
void your_function(const T& t,
    enable_if_t<is_something<T>::value, const string&>
s) {// ...
}

シナリオ 1 は、コンストラクターおよび変換演算子では機能しません。それらは戻り値の型を持たないためです。

シナリオ 2 では、パラメーターは名前がないままです。 ::type Dummy = BAR とすることもできますが、名前Dummy は重要ではなく、名前を付けることは "未参照のパラメーター" 警告をトリガーする可能性があります。 関数パラメーターの型 FOO、および既定の引数BAR を選択する必要があります。 int および 0 とすることもできますが、このコードのユーザーが範囲外の整数を誤って関数に渡し、その整数が無視される可能性があります。 代わりに、void **、および0 または nullptr のいずれかを使用することをお勧めします。void ** に変換できるものはほとんどないからです。

template <your_stuff>
your_return_type_if_present
yourfunction(args, typename enable_if<your_condition, void **>::type = nullptr) {// ...
}

シナリオ 2 は通常のコンストラクターでも機能します。 ただし、変換演算子は追加のパラメーターをとることができないため、シナリオ 2 は変換演算子では機能しません。 また、variadic コンストラクターでも機能しません。追加パラメーターを付加することによって、関数のパラメーターは非推定コンテキストをパックするため、enable_if の目的は達成できないからです。

シナリオ 3 は、Dummy という名前を使用しますが、これは任意です。 単に "typename = typename" だけで機能しますが、少し変に見えるかもしれないので、"dummy" という名前を使用できます。関数定義で使用される可能性のある名前は使用しないでください。 enable_if に型を指定しない場合、既定値は void になります。ユーザーは Dummy が何かを気にしなくていないため、これは非常に理にかなっています。 これは、変換演算子や variadic コンストラクターなど、すべてに対して機能します。

シナリオ 4 は、戻り値の型を持たないコンストラクターで機能するため、シナリオ 1 のラッピングの制約を解決します。 ただし、シナリオ 4 は非テンプレート関数の引数に限定されますが、このような引数は必ず利用できるわけではありません (テンプレート関数の引数でシナリオ 4 を使用すると、テンプレート引数の推定が機能しません)。

enable_if は強力ですが、誤って使用すると危険です。 enable_if の目的はオーバーロードの解決前に候補を除外することなので、誤って使用すると、その影響によって混乱が生じます。 以下に、推奨事項をいくつか示します。

  • コンパイル時に実装を選択するのに、enable_if を使用しないでください。 1 つの enable_ifCONDITION に対して記述し、別の enable_if を !CONDITION に記述しないでください。 代わりに、タグ ディスパッチ パターン (たとえば、反復子で与えられる強さによって実装を選択するアルゴリズム) を使用してください。

  • enable_if を使用して要件を強制しないでください。 テンプレート パラメーターを検証し、検証に失敗した場合は、別の実装を選択する代わりにエラーが発生する場合は、 static_assertを使用します。

  • 他の方法では、優れたコードが曖昧になってしまうようなオーバーロード セットがある場合には、enable_if を使用します。 ほとんどの場合、これは暗黙に変換されるコンストラクターで発生します。

この例では、C++ 標準ライブラリのテンプレート関数 std::make_pair()enable_if をどのように利用するかを示しています。

void func(const pair<int, int>&);

void func(const pair<string, string>&);

func(make_pair("foo", "bar"));

この例では、make_pair("foo", "bar")pair<const char *, const char *> を返します。 オーバーロード解決では、どの func() が必要かを決定する必要があります。 pair<A, B> は、pair<X, Y> から暗黙に変換されるコンストラクターを持っています。 これは新しいものではなく、C++98 にありました。 ただし C++98/03 では、暗黙に変換されるコンストラクターのシグネチャは、pair<int, int>(const pair<const char *, const char *>&) の場合でも必ず存在します。 オーバーロード解決では、コンストラクタを爆発的に増やすようなインスタンス化を試行しても問題になりません。const char * は暗黙にint に変換されず、関数定義がインスタンス化される前に、シグネチャを見ているだけだからです。 そのため、この例のコードは曖昧です。シグネチャは、pair<const char *, const char *>pair<int, int>pair<string, string> の両方に変換するために存在するからです。

C++11 は、enable_if を使用してこの曖昧さを解決しました。pair<A, B>(const pair<X, Y>&) が、以下の場合のみ、つまり const X&A に暗黙に変換され、const Y&B に暗黙に変換される場合のみ存在するようにしています。 これにより、オーバーロード解決は、pair<const char *, const char *>pair<int, int> に変換されないこと、および pair<string, string> となるオーバーロードが実行可能であることを決定できます。

要件

ヘッダー: <type_traits>

名前空間: std

関連項目

<type_traits>