以下代码无法编译(对于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()情况?

最佳答案

这里没有SFINAE。所有对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());


那么您会得到一个歧义错误,因为它与两个模板都匹配。

09-19 10:36