SFINAE working in return type but not as template parameter

半世苍凉 提交于 2019-11-25 23:47:48

问题


I already used the SFINAE idiom quite a few times and I got used to put my std::enable_if<> in template parameters rather than in return types. However, I came across some trivial case where it didn\'t work, and I\'m not sure why. First of all, here is my main:

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

Here is an implementation of foo that triggers the error:

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\";
}

And here is a supposedly equivalent piece of code that works fine:

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\";
}

My question is: why does the first implementation of foo triggers that error while the second one does not trigger it?

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>
          ^

EDIT :

Working code and faulty code.


回答1:


You should take a look at 14.5.6.1 Function template overloading (C++11 standard) where function templates equivalency is defined. In short, default template arguments are not considered, so in the 1st case you have the same function template defined twice. In the 2nd case you have expression referring template parameters used in the return type (again see 14.5.6.1/4). Since this expression is part of signature you get two different function template declarations and thus SFINAE get a chance to work.




回答2:


Values in the templates work:

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";
}



回答3:


The = ... of the template just gives a default parameter. This is not part of the actual signature which looks like

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

for both functions.

Depending on your needs, the most generic solution for this problem is using tag dispatching.

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{});
}


来源:https://stackoverflow.com/questions/15427667/sfinae-working-in-return-type-but-not-as-template-parameter

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!