具有两个参数包的函数模板重载决议
Function template overload resolution with two parameter packs
考虑以下代码:
#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
,但在 clang 7 上编译失败,因为调用 f(1)
不明确。
如果调用被替换为 f()
,两个编译器都无法编译,声称调用不明确。
如果参数包 class... T
被简单的参数 class T
替换(并且 T...
被 T
替换),两个编译器也声称有歧义。
第一个例子中哪个编译器符合标准?我想这归结为函数模板的特定部分排序规则,或者以这种方式使用双参数包是否已经不正确?
编辑:
我的理解是,如果第二个包可以从函数参数中推导出来,我的阅读中 [temp.param] 17.1/15 似乎明确允许这样做似乎是这样,因为T...
函数参数包。
也可以明确指定第一个参数包的参数,但不是第二个参数包,因此(在模板参数推导之后)至少一个参数包为空的情况并不总是如此。我不确定这是否会使程序格式错误,因为我不知道如何阅读,例如[temp.res] 17.7/8.3 在这种情况下。
gcc 和 clang 似乎都适用于双参数包本身,例如当删除第二个函数模板重载时,两个编译器都打印 1
。但这可能是格式错误的情况,不需要诊断。
此外,我假设使用 class 模板参数推导,一个可变参数 class 模板可以定义一个可变参数构造函数模板,这意味着构造函数候选类似于我的双参数包示例,并且作为据我了解,在该上下文中会发生相同的重载决议和模板参数推导。这个问题是由具有这种设置的另一个问题引起的:Variadic class template deduction fails with gcc 8.2, compiles with clang and msvc
另见有关讨论:
现在我也找到了问题 的答案,我认为这意味着 gcc 是错误的,调用应该被认为是模棱两可的,但我想让它验证这同样适用于这里方法。我也欢迎提供更详细的推理,因为函数模板偏序规则对我来说似乎很不清楚。
这里有两个问题。
首先,[temp.deduct.partial]/12(我也引用了这个例子,因为它与你的相似)说:
In most cases, deduction fails if not all template parameters have values, but for partial ordering purposes a template parameter may remain without a value provided it is not used in the types being used for partial ordering. [ Note: A template parameter used in a non-deduced context is considered used. — end note ] [ Example:
template <class T> T f(int); // #1
template <class T, class U> T f(U); // #2
void g() {
f<int>(1); // calls #1
}
— end example ]
,用于部分排序的类型是T...
The types used to determine the ordering depend on the context in which the partial ordering is done:
In the context of a function call, the types used are those function parameter types for which the function call has arguments.
...
所以第一个未命名的模板参数包class...
不影响偏序的结果。由于这两个函数模板没有其他区别,因此两者都不比另一个更专业,从而导致调用不明确。
这可能与GCC的bug 49505有关。
其次,即使第二个函数模板不存在,调用仍然应该是格式错误的。根据 [temp.arg.explicit]/3:
... A trailing template parameter pack not otherwise deduced will be deduced to an empty sequence of template arguments ...
只有 trailing 模板参数包可以推导为空包,而第一个未命名的模板参数包 class...
不是 trailing模板参数包。
考虑以下代码:
#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
,但在 clang 7 上编译失败,因为调用 f(1)
不明确。
如果调用被替换为 f()
,两个编译器都无法编译,声称调用不明确。
如果参数包 class... T
被简单的参数 class T
替换(并且 T...
被 T
替换),两个编译器也声称有歧义。
第一个例子中哪个编译器符合标准?我想这归结为函数模板的特定部分排序规则,或者以这种方式使用双参数包是否已经不正确?
编辑:
我的理解是,如果第二个包可以从函数参数中推导出来,我的阅读中 [temp.param] 17.1/15 似乎明确允许这样做似乎是这样,因为T...
函数参数包。
也可以明确指定第一个参数包的参数,但不是第二个参数包,因此(在模板参数推导之后)至少一个参数包为空的情况并不总是如此。我不确定这是否会使程序格式错误,因为我不知道如何阅读,例如[temp.res] 17.7/8.3 在这种情况下。
gcc 和 clang 似乎都适用于双参数包本身,例如当删除第二个函数模板重载时,两个编译器都打印 1
。但这可能是格式错误的情况,不需要诊断。
此外,我假设使用 class 模板参数推导,一个可变参数 class 模板可以定义一个可变参数构造函数模板,这意味着构造函数候选类似于我的双参数包示例,并且作为据我了解,在该上下文中会发生相同的重载决议和模板参数推导。这个问题是由具有这种设置的另一个问题引起的:Variadic class template deduction fails with gcc 8.2, compiles with clang and msvc
另见有关讨论:
现在我也找到了问题
这里有两个问题。
首先,[temp.deduct.partial]/12(我也引用了这个例子,因为它与你的相似)说:
,用于部分排序的类型是In most cases, deduction fails if not all template parameters have values, but for partial ordering purposes a template parameter may remain without a value provided it is not used in the types being used for partial ordering. [ Note: A template parameter used in a non-deduced context is considered used. — end note ] [ Example:
template <class T> T f(int); // #1 template <class T, class U> T f(U); // #2 void g() { f<int>(1); // calls #1 }
— end example ]
T...
The types used to determine the ordering depend on the context in which the partial ordering is done:
In the context of a function call, the types used are those function parameter types for which the function call has arguments.
...
所以第一个未命名的模板参数包class...
不影响偏序的结果。由于这两个函数模板没有其他区别,因此两者都不比另一个更专业,从而导致调用不明确。
这可能与GCC的bug 49505有关。
其次,即使第二个函数模板不存在,调用仍然应该是格式错误的。根据 [temp.arg.explicit]/3:
... A trailing template parameter pack not otherwise deduced will be deduced to an empty sequence of template arguments ...
只有 trailing 模板参数包可以推导为空包,而第一个未命名的模板参数包 class...
不是 trailing模板参数包。