涉及非推导参数包的函数指针参数类型的模板参数推导

14

这类似于这个问题,但更具体。这次,没有编译器按预期工作。

template<class T>
struct nondeduced
{
    using type = T;
};

template<class T>
using nondeduced_t = typename nondeduced<T>::type;

template<class... T, class U>
void f(void(*)(nondeduced_t<T>..., U)) {}

void g(int, char) { }

int main()
{
    f<int>(g); // error?
}

在上面的例子中,参数包T无法被推导出来,但编译器应该能够在对参数包T进行显式参数替换后推导出U(在本例中为单个int)。
上述代码也可以不使用nondeduced_t技巧而正常工作:
template<class... T, class U>
void f(void(*)(T..., U)) {}

因为参数包T已经处于无法推导的上下文中,根据[temp.deduct.type]p5

无法推导的上下文包括:

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

不幸的是,我测试过的所有编译器(g++/clang)都无法接受这段代码。值得注意的是,以下类似代码在g++和clang上都可以工作。

template<class... T>
void f(void(*)(nondeduced_t<T>..., char)) {}

再次强调,以下两种情况都不适用:

template<class... T>
void f(void(*)(T..., char)) {}

我的期望错了吗?

似乎规范中没有明确指定。 - Johannes Schaub - litb
所以你的目标是通过传入固定的 T... 来推断函数类型?这显然是可行的:template<class...Ts> struct outer { template<class U> void f( void(*)(Ts..., U ) ) {} };。@JohannesSchaub-litb,有什么不清楚的吗?非推导上下文集合已被列举出来,这是否是其中之一?标准描述了如何推断 U(参见上面的 outer),假设第一个参数没有被推断。 - Yakk - Adam Nevraumont
1
我认为在标准情况下 T... 应该是贪婪的,它应该不留任何内容给 U 去消费,这样就不会有歧义了。对吗? - Yakk - Adam Nevraumont
1
这个行为正常吗?链接 - BiagioF
显示剩余7条评论
1个回答

1

通过 [temp.deduct.type]p5,其中一个未推导上下文

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

作为模板函数的非最后一个参数的参数包永远不会被推导,但指定参数类型以禁用推导是完全正确的。例如:

template<class T1, class ... Types> void g1(Types ..., T1);

g1<int, int, int>(1,2,3);  // works by non-deduction
g1(1,2,3)                  // violate the rule above by non-deduced context

但是,即使保持模板参数不变,改变函数参数的顺序也会消除无法推导的上下文条件并打破参数包的无限扩展。例如:
template<class T1, class ... Types> void g1(T1, Types ...);
g1(1,2,3)                 // works because its a deduced context.

你的代码无法编译有两个原因:

  1. 函数参数的顺序创建了一个非推导上下文,导致在函数f中声明的参数包T的类型永远不会被推导。

  2. 模板参数T仅作为函数参数的限定符(例如nondeduced_t)出现,并没有直接指定为函数参数(这样允许参数推导)。

要使代码编译,必须将参数包的扩展放置在忽略nondeduced_t间接性的情况下,如下所示:

template<class... T,class U>
void f( void(*)(U,T...) ) { }

f(g);

或者更改模板参数的顺序,并在函数调用时指定模板参数,例如:

template<class U,class... T>
void f( void(*)(U,typename nondeduced<T>::type...) ) {}

f<int,char>(g);    

问题中有意地省略了必要信息。你不能通过改变前提条件来回答这个问题。 - Jamboree

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接