首页 > 解决方案 > 对不可演绎函数模板参数的误解

问题描述

来自C++ 模板 - 完整指南第 2 版

此外,此类参数不能有用地放置在模板参数包之后或出现在部分特化中,因为没有办法明确指定或推导出它们。

template<typename ...Ts, int N>
void f(double (&)[N+1], Ts ... ps); // useless declaration because N
                                    // cannot be specified or deduced

其中这些参数指的是(我认为)与那些永远无法推断的模板参数相对应的模板参数。即在上面的例子N中是不能推导的参数,因为N+1“太复杂而无法推导”。

但是为什么指定它是不可能的?我知道不可能指定N...Ts推导出来,但是为什么不能全部指定呢?换句话说,指定Ts=[int]N=2通过以下有什么问题?

    double x[3];
    f<int,2>(x,1);

标签: c++templatesc++17language-lawyertemplate-meta-programming

解决方案


即在上面的例子N中是不能推导的参数,因为N+1“太复杂而无法推导”。

形式上,这是[temp.deduct.type]/5.3

未推断的上下文是:

  • [...]
  • /5.3 非类型模板参数或绑定的数组,其中子表达式引用模板参数

正如以下问答中已经介绍的那样:

特别是在功能模板的模板头中

template<typename ...Ts, int N>
// ... function template

根据[temp.param]/14

函数模板的模板形参包后面不应有另一个模板形参,除非该模板形参可以从函数模板的形参类型列表([dcl.fct])推导出来或具有默认实参([temp.fct]) 。扣除])。

特别是根据函数模板的特殊规则(由于函数模板参数推导),模板参数N 必须可以从函数的参数列表中推导出来。根据[temp.deduct.type]/5.3,它不是,并且f在以下示例中永远不能调用(重载解决方案永远不会认为它是可行的候选者):

template<typename ...Ts, int N>
void f(double (&)[N+1], Ts ... ps);

而以下函数都可以通过重载解析找到:

template<typename ...Ts, int N>
void g(double (&)[N], Ts ... ps);   // N deducible from function parameter

template<typename ...Ts, int N = 2> // N has a default-template-argument
void h(double (&)[N+1], Ts ... ps);

但是为什么指定它是不可能的?

正如链接到问答中所讨论的那样,尽管“编译器支持这一点是有意义的”,但标准没有,并且领先的模板参数包贪婪地包含所有显式提供的模板参数,即使是那些作为一部分无效的模板参数扩展包(例如类型模板参数包的非类型模板参数)。


推荐阅读