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

Jam*_*ree 13 c++ language-lawyer variadic-templates c++11 template-argument-deduction

这类似于问题,但更具体的情况.这次,没有编译器按预期工作.

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?
}
Run Code Online (Sandbox Code Playgroud)

在上面的示例中,T无法推导出参数包,但编译器应该能够U在显式参数替换pack之后推导出T(即int在这种情况下为single ).

预计上述内容也可以在没有nondeduced_t诀窍的情况下工作:

template<class... T, class U>
void f(void(*)(T..., U)) {}
Run Code Online (Sandbox Code Playgroud)

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

未推断的上下文是:

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

不幸的是,我测试过的编译器(g ++/clang)都没有接受代码.值得注意的是,下面的内容适用于g ++和clang.

template<class... T>
void f(void(*)(nondeduced_t<T>..., char)) {}
Run Code Online (Sandbox Code Playgroud)

而且,这对两者都不起作用:

template<class... T>
void f(void(*)(T..., char)) {}
Run Code Online (Sandbox Code Playgroud)

我的期望是错的吗?

Jan*_*ans 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
Run Code Online (Sandbox Code Playgroud)

但是改变函数参数的顺序,甚至保留模板参数不变,删除了非推导的上下文条件并打破了参数包的无限扩展。例如

template<class T1, class ... Types> void g1(T1, Types ...);
g1(1,2,3)                 // works because its a deduced context.
Run Code Online (Sandbox Code Playgroud)

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

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

  2. 模板参数 T 仅作为函数参数中的限定符出现(例如nondeduced_t),而不直接指定为函数参数(允许参数推导)。

要使代码编译,您可以放置​​参数包的扩展,因为它忘记了nonduced_t间接,如

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

f(g);
Run Code Online (Sandbox Code Playgroud)

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

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

f<int,char>(g);    
Run Code Online (Sandbox Code Playgroud)