考虑到这段代码,模板参数推导如何决定最后一个函数调用的操作?
#include <iostream>
template<typename Ret, typename... Args>
Ret foo(Args&&...) {
std::cout << "not void\n";
return {};
}
template<typename... Args>
void foo(Args&&...) {
std::cout << "void\n";
}
int main() {
foo(3, 'a', 5.4); //(1): prints "void"
foo<int, char>(3, 'a', 5.4); //(2): prints "void"
foo<int>('a', 5.4); //(3): prints "not void"
foo<int>(3, 'a', 5.4); //(4): prints "not void"
}
(1) 看起来很简单。它无法推断返回类型,因此使用了 void
版本。
(2) 明确说明了一些参数的类型。第一个模板参数匹配第一个参数,第二个模板参数匹配第二个参数,第三个模板参数被推断。如果将 int
用作返回类型,则 char
将不匹配第一个参数。
(3) 与 (2) 相同,但第一个类型不匹配。因此,必须将其推导为返回类型,并使用两个参数来推导两个 Args
参数。
(4) 看起来有歧义。它像 (2) 和 (3) 一样明确指定了一个模板参数。模板参数与参数匹配,就像 (2) 一样。然而,它没有将其用作第一个并推导其他两个,而是将显式模板参数用作返回类型,并推导所有三个 Args
参数。
为什么(4)似乎在一半的情况下遵循(2),但随后使用另一种版本?我最好的猜测是填充单个模板参数比仅使用参数包更匹配。标准在哪里定义了这种行为?
这是使用GCC 4.8.0编译的。为方便起见,这里是Coliru上的测试运行。
然而,在Clang 3.1上,(4)无法编译,因为存在歧义(请参见注释)。这打开了其中一个编译器存在bug的可能性。使这种可能性更加成立的是,Visual Studio 2012年11月CTP编译器与Clang给出相同的结果,其中(4)存在歧义。