web-dev-qa-db-ja.com

C ++でSFINAEを機能させる方法

プロジェクトで関数SFINAEを頻繁に使用しており、次の2つのアプローチ(スタイル以外)に違いがあるかどうかはわかりません。

#include <cstdlib>
#include <type_traits>
#include <iostream>

template <class T, class = std::enable_if_t<std::is_same_v<T, int>>>
void foo()
{
    std::cout << "method 1" << std::endl;
}

template <class T, std::enable_if_t<std::is_same_v<T, double>>* = 0>
void foo()
{
    std::cout << "method 2" << std::endl;
}

int main()
{
    foo<int>();
    foo<double>();

    std::cout << "Done...";
    std::getchar();

    return EXIT_SUCCESS;
}

プログラムの出力は期待どおりです。

method 1
method 2
Done...

私はスタックオーバーフローでより頻繁に使用される方法2を見てきましたが、私は方法1を好みます。

これら2つのアプローチが異なる状況はありますか?

40
keith

私はスタックオーバーフローでより頻繁に使用される方法2を見てきましたが、私は方法1を好みます。

提案:方法2を優先します。

どちらの方法も単一の関数で機能します。問題は、同じシグネチャを持つ複数の関数があり、セットの1つの関数のみを有効にする場合に発生します。

foo()constexpr関数であると仮定)がtrueで、bar<T>()である場合に、foo()、バージョン1を有効にするとします。バージョン2、bar<T>()falseの場合。

template <typename T, typename = std::enable_if_t<true == bar<T>()>>
void foo () // version 1
 { }

template <typename T, typename = std::enable_if_t<false == bar<T>()>>
void foo () // version 2
 { }

あいまいさがあるため、コンパイルエラーが発生します。同じシグネチャを持つ2つのfoo()関数(デフォルトのテンプレートパラメーターはシグネチャを変更しません)。

しかし、次の解決策

template <typename T, std::enable_if_t<true == bar<T>(), bool> = true>
void foo () // version 1
 { }

template <typename T, std::enable_if_t<false == bar<T>(), bool> = true>
void foo () // version 2
 { }

sFINAEが関数のシグネチャを変更するため、機能します。

関連のない観察:3番目のメソッドもあります。戻り値の型を有効/無効にします(クラス/構造体コンストラクターを除く)

template <typename T>
std::enable_if_t<true == bar<T>()> foo () // version 1
 { }

template <typename T>
std::enable_if_t<false == bar<T>()> foo () // version 2
 { }

方法2と同様に、方法3は同じシグネチャを持つ代替関数の選択と互換性があります。

35
max66

max66の回答 に加えて、方法2を選択するもう1つの理由は、方法1を使用すると、(誤って)2番目のテンプレート引数として明示的な型パラメーターを渡し、SFINAEメカニズムを完全に無効にすることができるためです。これは、タイプミス、コピー/貼り付けエラー、またはより大きなテンプレートメカニズムの見落としとして発生する可能性があります。

#include <cstdlib>
#include <type_traits>
#include <iostream>

// NOTE: foo should only accept T=int
template <class T, class = std::enable_if_t<std::is_same_v<T, int>>>
void foo(){
    std::cout << "method 1" << std::endl;
}

int main(){

    // works fine
    foo<int>();

    // ERROR: subsitution failure, as expected
    // foo<double>();

    // Oops! also works, even though T != int :(
    foo<double, double>();

    return 0;
}

ライブデモはこちら

21
alter igel