代码之家  ›  专栏  ›  技术社区  ›  aschepler

模板参数包如何既有显式参数又有推导出的参数?

  •  2
  • aschepler  · 技术社区  · 7 年前

    G++、Clang++、MSVC(2018年前)均为 accept 下面的C++ 17代码,导致输出“无符号int”,然后输出“int”:

    #include <iostream>
    
    void print_type(int) { std::cout << "int\n"; }
    void print_type(unsigned int) { std::cout << "unsigned int\n"; }
    
    template <typename ...T>
    void print_types(T ...args)
    {
        (print_type(args),...);
    }
    
    int main()
    {
        print_types<unsigned int>(1, 1);
    }
    

    我同意这应该是这样的,但我很难在标准中找到原因和具体方法的描述。

    首先,有[temp.discrete]/2描述了在进行其余模板参数推导之前显式模板参数的处理:

    [t]在对给定函数模板的显式指定模板参数列表进行评估时,将执行以下步骤:

    • …参数不能多于参数,除非至少有一个参数是模板参数包,并且每个非包参数应有一个参数….

    • 指定的模板参数值将替换下面指定的相应模板参数。

    在这个例子中, unsigned int 当然是“指定的模板参数值”。但如果它的“对应模板参数” T 现在被替换,很难看到它以后如何成为更长的类型列表。

    对于模板参数推导过程,有[temp.reduce.call]/1:

    对于出现在 参数声明列表 ,对调用的每个剩余参数执行演绎,采用类型 P 声明者ID 作为相应的函数模板参数类型的函数参数包。每次演绎都会为函数参数包展开的模板参数包中的后续位置推导模板参数。

    我在这里取“调用的剩余参数”是指与不是最终函数参数包的函数参数相对应的参数之后的参数。但这意味着在我的示例中,第一个函数参数 1 用于推断 T=int . 这一扣减是否实际发生,但随后会被 T=unsigned int 从显式模板参数?

    或者,“调用的剩余参数”应该是指与最终函数参数包不对应的参数后的函数参数,以及与显式模板参数生成的参数类型对应的任何参数后的函数参数;以及“模板参数包中由函数参数包扩展的后续位置”iS应该是指任何由显式模板参数填充的位置之后的连续位置,但这还不清楚。如果是这样的话,还有一个与函数参数包相关联的参数类型列表也会让人困惑,但它仍然是一个函数参数包。

    [给出预期行为的另一个可能实现是:当一个或多个显式模板参数 A_1 A_k 对应于模板参数包 ,创建另一个模板参数包 More_P 相同类型,并替换 使用模板参数列表{ AA1 阿亚克 , More_P... }。然后 莫里普 可以像其他模板参数包一样推导。如果 莫里普 从不推导,在对语义进行评估之前,用一个空列表替换它的所有扩展,就像所有其他推导的替换一样。但在本标准中,这种解释的理由甚至更少。]

    我是否在标准中遗漏了一些更好地描述显式模板参数和推导的模板参数如何协同工作以形成一个模板参数包的单一列表的内容?

    1 回复  |  直到 7 年前
        1
  •  3
  •   xskxzr    7 年前

    它是 [temp.arg.explicit]/8 :

    模板参数推断可以扩展模板参数包对应的模板参数序列,即使序列包含显式指定的模板参数。[示例:

    template<class ... Types> void f(Types ... values);
    
    void g() {
      f<int*, float*>(0, 0, 0);     // Types is deduced to the sequence int*, float*, int
    }
    

    __结束示例__]