f1
和f2
的演绎是否形成不良?
template<class... T, class U>
void f1(T..., U){}
template<class... T>
void f2(T..., int){}
int main()
{
f1(1);
f2(1);
return 0;
}
g ++接受两者,clang只接受f2
,而msvc拒绝这两者。
相关标准措辞:
当函数参数包出现在非推导的上下文([temp.deduct.type])中时,永远不会推导出该参数包的类型。
未推断的上下文是:
- 函数参数包,不会出现在参数声明列表的末尾。
所以似乎MSVC拒绝两者都是正确的?
这是否意味着即使您明确指定模板args,模板的任何实例化都将是格式错误的?
f1<int>(1, 2); // ill-formed?
f2<int>(1, 2); // ill-formed?
如果是这样的话,为什么要在第一时间允许这样的声明呢?
这个具体问题DR1388有一个DR。显然,GCC和CLANG似乎还没有实施它CLANG DR1388。
这是否意味着即使您明确指定模板args,模板的任何实例化都将是格式错误的?
f1<int>(1, 2); // ill-formed? f2<int>(1, 2); // ill-formed?
如果是这样的话,为什么要在第一时间允许这样的声明呢?
否如果您明确指定模板参数,则不会发生扣减,因此上面显示的代码是合法的。
我自己研究过这个问题,我发现得到一个明确的答案是非常困难的。我可以说,f1(1)
应该被拒绝,但f2(1)
应该被接受。
那说:
正如您所指出的,未在列表末尾出现的函数参数包是非推导的上下文([temp.deduct.type] p5):
未推断的上下文是:
- ...
- 函数参数包,不会出现在参数声明列表的末尾。
和[temp.deduct.call] p1(通过CWG 1388修改)澄清了从未推断出这样的参数包。
当函数参数包出现在非推导的上下文中时,永远不会推导出该参数包的类型。
另外,[temp.arg.explicit] p3指定:
未以其他方式推导出的尾随模板参数包(14.5.3)将被推导为空的模板参数序列。
因此,考虑到f2(1)
调用:包T是一个尾随模板参数包(虽然它不是一个尾随的函数参数包),所以它推断为空包并且该调用有效。
但是,对于f1(1)
,包T也不是尾随模板参数包,因为它后跟U
,因此不会假定每个[temp.arg.explicit] p3为空包。因此,由于模板参数包T不能用于调用f1(1)
,因此它不应该参与重载解析并且调用应该失败。
请注意,在其他讨论中提出了几个类似的问题/示例,但它们都有微妙的不同:
f(0)
和CWG 1388的示例代码中调用CWG 1399是有效的,因为有问题的包是一个尾随模板参数包,所以它属于我上面提到的情况。以下是CWG 1399的代码:
template <class... T>
void f(T..., int, T...) { }
int main() {
f(0); // OK
f<int>(0,0,0); // OK
f(0,0,0); // error
}
template<typename... T> void f(typename T::type...) {
}
int main() {
f<>();
}