Why Can't These Templatized Functions Take No Arguments?

半城伤御伤魂 提交于 2019-12-12 04:06:58

问题


I am trying to use a couple templatized functions for Substitution Fail Is Not An Error(SFINAE). And I can do it like this:

template<typename R, typename S = decltype(declval<R>().test())> static true_type Test(R*);
template<typename R> static false_type Test(...);

But I'm not understanding how the argument makes this SNFIAE work. It seems like I should just be able to remove the arguments and the template selection would work the exact same way:

template<typename R, typename S = decltype(declval<R>().test())> static true_type Test();
template<typename R> static false_type Test();

But it does not, I get:

Call of overloaded 'Test()' is ambiguous

What is it about these arguments that make this SFINAE work?


回答1:


Your second example fails to compile, since there are two overloads of Test with identical signature, becasue defaulted template type arguments are not part of function signature. That is not allowed.

Your first example works in followign manner:

When type R does have a function test in it, both Test become valid overload candidates. However, ellipsis functions have lower rank than non-ellipsis ones, and thus compiler selects the first overload, returning true_type.

When R does not have test on it, the first overload is excluded from overload resolution set (SFINAE at works). You are left with only the second one, which returns false_type.




回答2:


The question has been answered but maybe it's useful to go into a deeper explanation.

Hopefully this annotated program will make things clearer:

#include <utility>
#include <iostream>

// define the template function Test<R> if and only if the expression
// std::declval<R>().test()
// is a valid expression.
// in which case Test<R, decltype(std::declval<R>().test())>(0) will be preferrable to... (see after)
template<typename R, typename S = decltype(std::declval<R>().test())> 
  static std::true_type Test(R*);

// ...the template function Test<R>(...)
// because any function overload with specific arguments is preferred to this
template<typename R> static std::false_type Test(...);


struct foo
{
  void test();
};

struct bar
{
  // no test() method
//  void test();
};


// has_test<T> deduces the type that would have been returned from Test<T ... with possibly defaulted args here>(0)
// The actual Test<T>(0) will be the best candidate available
// For foo, it's Test<foo, decltype(std::declval<R>().test())>(foo*)
// which deduces to
// Test<foo, void>(foo*)
// because that's a better match than Test<foo>(...)
//
// for bar it's Test<bar>(...)
// because Test<bar, /*error deducing type*/>(bar*)
// is discarded as a candidate, due to SFNAE
//
template<class T>
constexpr bool has_test = decltype(Test<T>(0))::value;


int main()
{
  std::cout << has_test<foo> << std::endl;
  std::cout << has_test<bar> << std::endl;
}


来源:https://stackoverflow.com/questions/37075331/why-cant-these-templatized-functions-take-no-arguments

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