涉及非推导参数包

时间:2016-08-23 15:05:51

标签: c++ c++11 language-lawyer variadic-templates template-deduction

这类似于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)) {}

我的期望是错的吗?

1 个答案:

答案 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.

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

  1. 函数参数的顺序创建非推导上下文,这会导致函数中所述模式中参数包 T 的类型f 永远不会被推断出来。

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

  3. 要进行代码编译,您可以放置​​参数包的扩展,因为它会忘记 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);