Почему эти шаблонные функции не могут принимать аргументы?
Я пытаюсь использовать пару templatized функций для Ошибка замещения не является ошибкой (SFINAE). И я могу сделать это вот так:
template<typename R, typename S = decltype(declval<R>().test())> static true_type Test(R*);
template<typename R> static false_type Test(...);
Но я не понимаю, как аргумент делает этот SNFIAE. Похоже, я должен просто удалить аргументы, и выбор шаблона будет работать точно так же:
template<typename R, typename S = decltype(declval<R>().test())> static true_type Test();
template<typename R> static false_type Test();
Но это не так, я получаю:
Вызов перегруженного 'Test()' неоднозначен
Что происходит с этими аргументами, которые делают эту работу SFINAE?
Ответы
Ответ 1
Второй пример не скомпилирован, так как есть две перегрузки Test
с одинаковой сигнатурой, поскольку аргументы типа шаблона по умолчанию не являются частью сигнатуры функции. Это не разрешено.
Ваш первый пример работает следующим образом:
Когда тип R
имеет в нем функцию Test
, оба Test
становятся действительными кандидатами перегрузки. Однако функции эллипсиса имеют более низкий ранг, чем неэллиптические, и, таким образом, компилятор выбирает первую перегрузку, возвращая true_type
.
Когда R не имеет на нем Test
, первая перегрузка исключается из набора разрешений перегрузки (SFINAE при работе). Осталось только второе, которое возвращает false_type
.
Ответ 2
Вопрос был дан ответ, но, возможно, полезно углубиться в более глубокое объяснение.
Надеюсь, эта аннотированная программа сделает все более ясным:
#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 Test<foo, decltype(std::declval<R>().test())>(foo*)
// which deduces to
// Test<foo, void>(foo*)
// because that a better match than Test<foo>(...)
//
// for bar it 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;
}