42

私はすでにSFINAEイディオムをかなりの回数使用してstd::enable_if<>おり、戻り型ではなくテンプレートパラメーターに入れることに慣れていました。しかし、それが機能しないという些細なケースに出くわしました。その理由はわかりません。まず第一に、ここに私のメインがあります:

int main()
{
    foo(5);
    foo(3.4);
}

fooエラーをトリガーする実装は次のとおりです。

template<typename T,
         typename = typename std::enable_if<std::is_integral<T>::value>::type>
auto foo(T)
    -> void
{
    std::cout << "I'm an integer!\n";
}

template<typename T,
         typename = typename std::enable_if<std::is_floating_point<T>::value>::type>
auto foo(T)
    -> void
{
    std::cout << "I'm a floating point number!\n";
}

そして、これはおそらく正常に機能する同等のコードです。

template<typename T>
auto foo(T)
    -> typename std::enable_if<std::is_integral<T>::value>::type
{
    std::cout << "I'm an integrer!\n";
}

template<typename T>
auto foo(T)
    -> typename std::enable_if<std::is_floating_point<T>::value>::type
{
    std::cout << "I'm a floating point number!\n";
}

私の質問は、最初の実装fooがそのエラーをトリガーするのに、2番目の実装はそれをトリガーしないのはなぜですか?

main.cpp:14:6: error: redefinition of 'template<class T, class> void foo(T)'
 auto foo(T)
      ^
main.cpp:6:6: note: 'template<class T, class> void foo(T)' previously declared here
 auto foo(T)
      ^
main.cpp: In function 'int main()':
main.cpp:23:12: error: no matching function for call to 'foo(double)'
     foo(3.4);
            ^
main.cpp:6:6: note: candidate: template<class T, class> void foo(T)
 auto foo(T)
      ^
main.cpp:6:6: note:   template argument deduction/substitution failed:
main.cpp:5:10: error: no type named 'type' in 'struct std::enable_if<false, void>'
          typename = typename std::enable_if<std::is_integral<T>::value>::type>
          ^

編集

動作中のコード障害のあるコード

4

3 に答える 3

41

14.5.6.1 Function template overloading関数テンプレートの同等性が定義されている(C ++ 11標準)を確認する必要があります。つまり、デフォルトのテンプレート引数は考慮されないため、最初のケースでは、同じ関数テンプレートが2回定義されています。2番目のケースでは、戻り型で使用されるテンプレートパラメーターを参照する式があります(ここでも14.5.6.1 / 4を参照)。この式は署名の一部であるため、2つの異なる関数テンプレート宣言を取得し、SFINAEが機能する機会を得ることができます。

于 2013-03-15T08:51:18.397 に答える
15

テンプレートの値は機能します:

template<typename T,
         typename std::enable_if<std::is_integral<T>::value, int>::type = 0>
auto foo(T)
    -> void
{
    std::cout << "I'm an integer!\n";
}

template<typename T,
         typename std::enable_if<std::is_floating_point<T>::value, int>::type = 0>
auto foo(T)
    -> void
{
    std::cout << "I'm a floating point number!\n";
}
于 2016-07-06T03:27:28.367 に答える
12

= ...テンプレートのは、デフォルトのパラメータを提供するだけです。これは、次のような実際の署名の一部ではありません

template<typename T, typename>
auto foo(T a);

両方の機能について。

ニーズに応じて、この問題の最も一般的な解決策は、タグディスパッチを使用することです。

struct integral_tag { typedef integral_tag category; };
struct floating_tag { typedef floating_tag category; };

template <typename T> struct foo_tag
: std::conditional<std::is_integral<T>::value, integral_tag,
                    typename std::conditional<std::is_floating_point<T>::value, floating_tag,
                                               std::false_type>::type>::type {};

template<typename T>
T foo_impl(T a, integral_tag) { return a; }

template<typename T>
T foo_impl(T a, floating_tag) { return a; }

template <typename T>
T foo(T a)
{
  static_assert(!std::is_base_of<std::false_type, foo_tag<T> >::value,
                 "T must be either floating point or integral");
  return foo_impl(a, typename foo_tag<T>::category{});
}

struct bigint {};
template<> struct foo_tag<bigint> : integral_tag {};

int main()
{
  //foo("x"); // produces a nice error message
  foo(1);
  foo(1.5);
  foo(bigint{});
}
于 2013-03-15T09:10:49.320 に答える