Почему SFINAE применяется только к одной из этих двух, казалось бы, идентичных функций? - PullRequest
0 голосов
/ 02 марта 2019

Следующий код не компилируется (С clang 7.0, --std = c ++ 17):

struct Foo {
    void foo() {}
};
struct Bar {
    void bar() {}
};
struct Both {
    void foo() {}
    void bar() {}
};

template <typename T, typename>
void DoStuff(T input) {
    input.foo();
}

template <typename T, typename...>
void DoStuff(T input) {
    input.bar();
}

int main(int argc, char** argv) {
    DoStuff(Foo());
    return 0;
}

Ошибка:

<source>:19:11: error: no member named 'bar' in 'Foo'
    input.bar();
    ~~~~~ ^

<source>:23:5: note: in instantiation of function template specialization 'DoStuff<Foo>' requested here
    DoStuff(Foo());

Однако, если выизмените Foo() на Bar() (или Both()), он компилируется просто отлично.В случае Bar() это показывает, что SFINAE вступает в силу;в случае Both() это показывает, что перегрузка typename... имеет меньший приоритет, поэтому другой вариант выбирается, когда оба применяются.

Но я не понимаю, почему SFINAE применяется к Bar()дело, но не дело Foo()?

1 Ответ

0 голосов
/ 02 марта 2019

Здесь СФИНА не происходит.Все вызовы DoStuff будут вызывать

template <typename T, typename...>
void DoStuff(T input) {
    input.bar();
}

Причина этого в том, что

template <typename T, typename>
void DoStuff(T input) {
    input.foo();
}

требует два параметра шаблона, в то время как

template <typename T, typename...>
void DoStuff(T input) {
    input.bar();
}

работает для1 или более параметров шаблона (пустые пакеты могут быть пустыми).Поэтому, когда вы звоните

DoStuff(Foo());
// or
DoStuff(Bar());
//or
DoStuff(Both());

, вы можете вывести только один шаблонный параметр, и единственным приемлемым кандидатом является

template <typename T, typename...>
void DoStuff(T input) {
    input.bar();
}

Если вы использовали

DoStuff<Foo, any_other_type>(Foo());

, товы получите ошибку неоднозначности, поскольку она соответствует обоим шаблонам.

...