【问题标题】:Function template overload resolution with two parameter packs具有两个参数包的函数模板重载解析
【发布时间】:2019-04-20 20:10:49
【问题描述】:

考虑以下代码:

#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 似乎都可以使用双参数包本身,例如当第二个函数模板重载被移除时,两个编译器都会打印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 是错误的,并且该调用应被视为模棱两可,但我希望它验证这同样适用于此处。我也欢迎更详细的推理,因为函数模板部分排序规则对我来说似乎很不清楚。

【问题讨论】:

  • 第一个 f 在我看来格式不正确。编译器如何确定性地推断出这两个包?
  • @RichardHodges - {}{int}(或任何其他作为函数参数传递的)。一包可以推断为空的......我想。
  • @RichardHodges 我添加了一些解释,为什么我认为双包本身已经不正确。

标签: c++ language-lawyer overload-resolution template-argument-deduction


【解决方案1】:

这里有两个问题。


首先,[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 有关。


第二,即使第二个函数模板不存在,调用仍然应该是非良构的。根据[temp.arg.explicit]/3

...一个 trailing 模板参数包没有以其他方式推导将被推导为模板参数的空序列...

只有trailing模板参数包可以推导出为空包,而第一个未命名的模板参数包class...不是trailing模板参数包。

GCC (bug 69623) 和 Clang (bug 26435) 都有针对此问题的错误。

【讨论】:

猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-10-23
  • 1970-01-01
  • 1970-01-01
  • 2019-01-03
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多