这类似于question,但更具体的情况。这一次,编译器没有按预期工作。
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
,但编译器应该能够在显式参数替换pack U
之后推导出T
(即单{{1}在这种情况下)。
上述内容预计也会在没有int
技巧的情况下发挥作用:
nondeduced_t
因为参数包template<class... T, class U>
void f(void(*)(T..., U)) {}
已经在非推导的上下文中
[temp.deduct.type]p5
未推断的上下文是:
- 不在参数声明列表末尾出现的函数参数包。
不幸的是,我测试过的编译器(g ++ / clang)都没有接受代码。 值得注意的是,下面的内容适用于g ++和&amp;铛。
T
同样,这对两者都不起作用:
template<class... T>
void f(void(*)(nondeduced_t<T>..., char)) {}
我的期望是错的吗?
答案 0 :(得分:1)
[temp.deduct.type]p5其中一个非推断的上下文是
函数参数包,它不会出现在参数声明列表的末尾。
从不推断出不作为模板函数的最后一个参数出现的参数包,但完全正确地指定禁用推导的参数类型。 e.g
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
但是,即使保留模板参数,也要更改函数参数的顺序,删除非推导的上下文条件并打破参数包的无限扩展。 e.g
template<class T1, class ... Types> void g1(T1, Types ...);
g1(1,2,3) // works because its a deduced context.
您的代码无法编译的原因有两个:
函数参数的顺序创建非推导上下文,这会导致函数中所述模式中参数包 T 的类型f 永远不会被推断出来。
模板参数T仅作为函数参数中的限定符出现(例如 nondeduced_t ),而不是直接指定为函数参数(允许参数推导)。
要进行代码编译,您可以放置参数包的扩展,因为它会忘记 nondeduced_t indirect,因为
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);