问题
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