函数参数包的模板参数推导后跟其他参数

问题描述 投票:7回答:2

f1f2的演绎是否形成不良?

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.call]

当函数参数包出现在非推导的上下文([temp.deduct.type])中时,永远不会推导出该参数包的类型。

[temp.deduct.type]p5

未推断的上下文是:

  • 函数参数包,不会出现在参数声明列表的末尾。

所以似乎MSVC拒绝两者都是正确的?

这是否意味着即使您明确指定模板args,模板的任何实例化都将是格式错误的?

f1<int>(1, 2); // ill-formed?
f2<int>(1, 2); // ill-formed?

如果是这样的话,为什么要在第一时间允许这样的声明呢?

c++ c++11 variadic-templates template-deduction
2个回答
2
投票

这个具体问题DR1388有一个DR。显然,GCC和CLANG似乎还没有实施它CLANG DR1388

这是否意味着即使您明确指定模板args,模板的任何实例化都将是格式错误的?

f1<int>(1, 2); // ill-formed?
f2<int>(1, 2); // ill-formed?

如果是这样的话,为什么要在第一时间允许这样的声明呢?

否如果您明确指定模板参数,则不会发生扣减,因此上面显示的代码是合法的。


0
投票

我自己研究过这个问题,我发现得到一个明确的答案是非常困难的。我可以说,f1(1)应该被拒绝,但f2(1)应该被接受。

那说:

  • clang ++ - 5.0接受两者
  • g ++ - 6接受两者
  • EDG 4.14拒绝两者

正如您所指出的,未在列表末尾出现的函数参数包是非推导的上下文([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 }
  • LLVM bug 21774讨论中的示例代码类似于CWG 1399示例,该包是一个尾随模板参数包。同样对于这个question
  • CWG 2055,尚未解决,涉及类似的测试案例。无论什么时候解决,它的解决方案都可能会对这个问题中的例子的正确行为有所了解。以下是CWG 2055中提到的问题: 目前尚不清楚[标准的当前措辞]是否允许以下​​示例: template<typename... T> void f(typename T::type...) { } int main() { f<>(); }
© www.soinside.com 2019 - 2024. All rights reserved.