プロジェクトで関数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つのアプローチが異なる状況はありますか?
私はスタックオーバーフローでより頻繁に使用される方法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は同じシグネチャを持つ代替関数の選択と互換性があります。
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;
}