web-dev-qa-db-ja.com

「表現SFINAE」とは?

http://blogs.msdn.com/b/vcblog/archive/2011/09/12/10209291.aspx で、VC++チームは、まだC++ 11を実装していないことを正式に宣言していますコア機能「Expression SFINAE」。ただし、 http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2634.html からコピーされた次のコード例は、VC++コンパイラで受け入れられます。

例1:

template <int I> struct A {};

char xxx(int);
char xxx(float);

template <class T> A<sizeof(xxx((T)0))> f(T){}

int main()
{
    f(1);
}

例2:

struct X {};
struct Y 
{
    Y(X){}
};

template <class T> auto f(T t1, T t2) -> decltype(t1 + t2); // #1
X f(Y, Y);  // #2

X x1, x2;
X x3 = f(x1, x2);  // deduction fails on #1 (cannot add X+X), calls #2

私の質問は、「式SFINAE」とは何ですか?

56
xmllmx

SFINAEという表現は、あなたがリンクした論文でかなりよく説明されていると思います。式では [〜#〜] sfinae [〜#〜] です。 decltype内の式が有効でない場合は、VIPオーバーロードのラウンジから関数をキックします。規範的な表現はこの回答の最後にあります。

VC++に関するメモ:彼らはそれを実装していません完全に。単純な式では機能するかもしれませんが、機能しない場合もあります。失敗する例については、コメントのディスカッション この回答について を参照してください。簡単にするために、これは機能しません。

#include <iostream>

// catch-all case
void test(...)
{
  std::cout << "Couldn't call\n";
}

// catch when C is a reference-to-class type and F is a member function pointer
template<class C, class F>
auto test(C c, F f) -> decltype((c.*f)(), void()) // 'C' is reference type
{
  std::cout << "Could call on reference\n";
}

// catch when C is a pointer-to-class type and F is a member function pointer
template<class C, class F>
auto test(C c, F f) -> decltype((c->*f)(), void()) // 'C' is pointer type
{
  std::cout << "Could call on pointer\n";
}

struct X{
  void f(){}
};

int main(){
  X x;
  test(x, &X::f);
  test(&x, &X::f);
  test(42, 1337);
}

Clangの場合、これは期待される結果を出力します。

参照して呼び出すことができます
ポインタで呼び出すことができました
電話できませんでした

MSVCを使用すると、コンパイラエラーが発生します。

 1> src\main.cpp(20):エラーC2995: '' unknown-type 'test(C、F)':関数テンプレートはすでに定義されています
 1> src\main。 cpp(11): 'test'の宣言を参照

また、GCC 4.7.1はタスクに完全に対応していないようです。

 source.cpp: 'template decltype((c。* f()、void()))test(C、F)の代わりに[with C = X *; F = void(X :: *)()] ':
 source.cpp:29:17:ここから必要
 source.cpp:11:6:エラー:メンバーポインターを適用できません' f 'から' c 'へ、これは非クラスタイプ' X * 'です。
 source.cpp:' template decltype((c。* f()、void()))の代わりにtest(C 、F)[with C = int; F = int] ':
 source.cpp:30:16:ここから必要
 source.cpp:11:6:エラー:' f 'はメンバーポインターとして使用できません。タイプは「int」です

式SFINAEの一般的な用途は、クラスが特定のメンバー関数を実行しているかどうかをチェックする特性など、特性を定義する場合です。

struct has_member_begin_test{
  template<class U>
  static auto test(U* p) -> decltype(p->begin(), std::true_type());
  template<class>
  static auto test(...) -> std::false_type;
};

template<class T>
struct has_member_begin
  : decltype(has_member_begin_test::test<T>(0)) {};

実例 (驚くべきことに、GCC 4.7.1で再び動作します。)

この私の答え も参照してください。これは、別の環境で同じ手法を使用します(特性なし)。


規範的な表現:

§14.8.2 [temp.deduct]

p6テンプレート引数の推定プロセスの特定の時点で、テンプレートパラメーターを使用する関数型を取得し、それらのテンプレートパラメーターを対応するテンプレート引数で置き換える必要がありますこれは、明示的に指定されたテンプレート引数が関数タイプに代入されると、テンプレート引数の控除の始めに行われ、再び、テンプレート引数がデフォルトの引数から推定または取得されたものが代入されます

p7関数タイプとテンプレートパラメータ宣言で使用されるすべてのタイプと式で置換が行われます。 式には、定数の式だけでなく、配列の境界や非型テンプレート引数として表示されるものだけでなく、一般的な式も含まれます式(つまり、非定数式)insidesizeofdecltype、および非定数式を許可するその他のコンテキスト。

p8置換の結果、型または式が無効になると、型の推定は失敗します。無効な型または式は、置換された引数を使用して記述された場合に形式が正しくないものです。 [...]

70
Xeo