给定这段代码,模板参数推断如何决定最后一次函数调用的操作?
#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)似乎很简单。它不能推导出返回类型,因此使用val
版本。
(2)明确说明一些参数的类型。第一个模板参数匹配第一个参数,第二个模板参数匹配第二个参数,推导出第三个模板参数。如果int
用于返回类型,则char
与第一个参数不匹配。
(3) 执行与(2)相同的操作,但第一个类型不匹配。因此,必须将其推断为返回类型和用于推断两个Args
参数的两个参数。
(4) 似乎模棱两可。它显式地指定了一个模板参数,就像(2)和(3)一样。模板参数与参数匹配,如(2)。但是,它不使用该参数作为第一个参数并推断其他两个参数,而是使用显式模板参数作为返回类型并推断所有三个Args
参数。
为什么(4)似乎有一半跟在(2)后面,但后来使用了另一个版本?我最好的猜测是,正在填充的单个模板参数比仅仅参数包更匹配。标准在哪里定义这种行为?
这是使用GCC4.8编译的。为了方便起见,这里有一个在Coliru上运行的测试。
然而,在Clang 3.1上,(4)由于含糊不清而没有编译(见注释)。这打开了这两个编译器中的一个有错误的可能性。使这种可能性更有可能的是,Visual Studio 2012 11月CTP编译器给出了与Clang相同的结果,其中(4)是模糊的。
我相信Clang是正确的(仍在3.3 SVN中),根据偏序规则,(4)中的调用是不明确的-两个函数模板都是可行的,并且都比另一个更专业。
注意,通过将变量包转换为单个参数,可以强制GCC提供相同的输出:
#include <iostream>
template<typename Ret, typename T>
Ret foo(T&&) {
std::cout << "not void\n";
return {};
}
template<typename T>
void foo(T&&) {
std::cout << "void\n";
}
int main() {
foo<int>(3);
}
输出:
main.cpp: In function 'int main()':
main.cpp:15:15: error: call of overloaded 'foo(int)' is ambiguous
活生生的例子。