下面的代码适用于gcc-4.8.2
#include <iostream>
using namespace std;
template<typename... Args>
void func(Args... args, int optional = 0)
{
cout << optional << endl;
}
int main()
{
func(1);
func(2.1f); // converts 2.1 to int as 'optional' parameter
func<float>(3.3f); // Fine, prints '0'
func(); // gcc OK, fails to compile with clang-3.5
}
它输出:
$ ./a.out
1
2
0
0
但如果使用 clang-3.5 编译失败,
test_variadic.cpp:15:2: error: no matching function for call to 'func'
func();
^~~~
test_variadic.cpp:5:6: note: candidate function template not viable: requires at least argument 'args', but no arguments were provided
void func(Args... args, int optional = 0)
^
Clang至少警告了从浮点数到整数的隐式转换。 很好,我们可以通过调用 func<float>
将 float 参数放入模板包来纠正这一点。 所以,如果我注释掉func()
,它可以很好地编译。
我在标准中找不到任何明确说可变参数模板包必须是参数声明子句中的最后一件事,只是它变成了一个非推导的上下文。
我的困惑来自于为什么叮当不喜欢func()
,当func(1)
是完全可以接受的。 我可以手动定义func(int optional = 4) { cout << optional << endl; }
并且一切都很好(但是传递int
时不是模板化函数,而是我在 clang 和 gcc 中正确获得了专门的func()
。 什么是限制使用func()
的 clang 强制执行?
实际上被稍微错位的[temp.arg.explicit]/3所覆盖:
尾随模板参数包 (14.5.3) 不是 否则推导将被推导出为模板参数的空序列。
模板参数包是尾随的,因此在除 func<float>(3.3f)
之外的所有调用中都推导出为空包,也就是说,它们都是有效的(并且 Clang 从 3.5 开始编译它们)。
但是,一旦我们将模板的声明调整为
template <typename... Args, typename=void>
void func(Args... args, int optional = 0)
现在,上述引用不适用(因为Args
没有尾随),而 [temp.deduct.call]/1 则适用:
当函数参数包出现在非推导上下文中时 (14.8.2.5),永远不会推断出该参数包的类型。
(即这应该产生扣除失败。