2016-05-06 2 views
0

Я пытаюсь использовать пару templatized функций для Substitution Fail Is Not An Error(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?

ответ

3

Ваш второй пример не скомпилирован, так как есть две перегрузки Test с одинаковой подписью, поскольку аргументы типа шаблона по умолчанию не являются частью сигнатуры функции. Это недопустимо.

Ваш первый пример работает followign образом:

Когда тип R имеет функцию test в нем, как Test становятся действительными кандидатами от перегрузки. Однако функции эллипсиса имеют более низкий ранг, чем неэллиптические, и, таким образом, компилятор выбирает первую перегрузку, возвращая true_type.

Если у R нет test, первая перегрузка исключается из набора разрешений перегрузки (SFINAE при работах). Осталось только второе, которое возвращает false_type.

+0

Но помимо этого, есть что-то особенное об этих типах аргументов, что вызывает их оценивать в правильном порядке. Я не понимаю, почему компилятор предпочитает 'R *' '...'. Вы можете это объяснить? –

+0

@JonathanMee, добавит к ответу. – SergeyA

+0

@JonathanMee, чтобы выразить это просто, (...) является наименее предпочтительным совпадением с любым списком аргументов при выполнении разрешения перегрузки. Это по дизайну, и это предусмотрено стандартом. Кстати, это также наименее предпочтительный способ реализации функции с переменными аргументами :-) –

1

Вопрос был дан ответ, но, возможно, полезно углубиться в более глубокое объяснение.

Надеюсь, эта аннотированный программа будет делать вещи яснее:

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