考虑以下代码:
#include<iostream>
template<class..., class... T>
int f(T...) { return 1; }
template<class... T>
int f(T...) { return 2; }
int main()
{
std::cout << f(1);
}
它在gcc 8.2上编译并打印1
,但由于调用f(1)
含糊不清,因此无法在clang 7上编译。
如果调用被f()
替换,则两个编译器均无法编译,声称该调用不明确。
如果将参数包class... T
替换为简单的参数class T
(并且将T...
替换为T
),则两个编译器也会声明模棱两可。
在第一个示例中,哪个编译器符合标准?我想这归结为功能模板的特定部分排序规则,或者以这种方式使用双参数包已经不正确吗?
编辑:
我的理解是,双重包装本身不是畸形的,因为在我的阅读中,[temp.param] 17.1 / 15似乎明确允许这种情况,如果第二个包装可以从函数参数中推导出来。情况是由于T...
函数参数包。
还可以显式地指定第一个参数包的参数,尽管不是第二个,所以(在扣除模板参数后)至少一个参数包不是空的并不总是这样。我不确定这是否会使程序格式错误,因为我不知道如何读取例如[temp.res]在这种情况下为17.7 / 8.3。
gcc和clang似乎都适合使用double参数包本身,例如消除第二个功能模板重载后,两个编译器都将打印1
。但这可能是格式错误,不需要诊断的情况。
此外,我假设通过类模板参数推导,可变参数类模板可以定义可变参数构造函数模板,这暗示着类似于我的双参数包示例的构造函数候选,并且据我了解相同的重载分辨率和模板在这种情况下进行论证推论。此问题是由另一个具有以下设置的问题引起的:Variadic class template deduction fails with gcc 8.2, compiles with clang and msvc 另请参阅以获取有关此内容的讨论:Deduction guides and variadic class templates with variadic template constructors - mismatched argument pack lengths
现在,我还找到了问题Deduction guide and variadic templates的答案,我认为这意味着gcc错误,并且该调用应被认为是模棱两可的,但是我想验证它是否适用于此处。我也欢迎更详细的推理,因为对我而言,函数模板的部分排序规则似乎非常不清楚。
答案 0 :(得分:6)
这里有两个问题。
首先,[temp.deduct.partial]/12(由于与您的示例类似,我也引用了该示例)说:
在大多数情况下,如果不是所有模板参数都具有值,则推导将失败,但是出于部分排序的目的,如果模板参数未用于部分排序的类型中,则可以保留不带值的模板参数。 [注意:在非推导上下文中使用的模板参数被视为已使用。 —尾注] [示例:
template <class T> T f(int); // #1 template <class T, class U> T f(U); // #2 void g() { f<int>(1); // calls #1 }
-示例]
根据[temp.deduct.partial]/3,用于部分排序的类型为T...
:
用于确定排序的类型取决于完成部分排序的上下文:
在函数调用的上下文中,使用的类型是函数调用具有参数的那些函数参数类型。
...
因此,第一个未命名模板参数包class...
不会影响部分排序的结果。由于这两个功能模板没有其他差异,因此两者都不比另一个更专业,从而导致模棱两可的调用。
此可能与GCC的bug 49505有关。
第二,即使第二个功能模板不存在,调用仍应是错误的形式。根据{{3}}:
...否则推导的跟踪模板参数包将被推导为空的模板参数序列...
只能将 trailing 模板参数包推导出为空包,而第一个未命名的模板参数包class...
不是 trailing 模板参数包。
GCC([temp.arg.explicit]/3)和Clang(bug 69623)均存在此问题的错误。