模板参数包如何同时具有显式参数和推导参数?

时间:2018-07-14 14:05:16

标签: c++ language-lawyer variadic-templates c++17

g ++,clang ++和MSVC(2018年前)全部accept以下C ++ 17代码,导致输出“ unsigned 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.deduct] / 2描述了显式模板参数的处理:

  

[T]相对于给定功能模板评估显式指定的模板参数列表时,将执行以下步骤:

     
      
  • ...除非有至少一个参数是模板参数包,并且每个非包装参数都必须有一个参数,否则参数的数量不得超过参数。...

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

  •   

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

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

  

对于出现在 parameter-declaration-list 末尾的函数参数包,将对调用的每个剩余参数执行推导,并采用<函数参数包的em> declarator-id 作为相应的函数模板参数类型。每个推论都会推导模板参数包中由函数参数包扩展的后续位置的模板参数。

我在这里将“调用的剩余参数”表示为对应于不是最终函数参数包的函数参数的参数之后的参数。但这意味着在我的示例中,第一个函数参数P用于推导1。这种推论是否真的发生了,但是却被显式模板参数中的T=int丢弃/覆盖了?

或者“调用的剩余参数”应该是指不对应于最终函数参数包的函数参数之后以及对应于从显式模板参数生成的参数类型的参数之后的函数参数; “功能参数包扩展的模板参数包中的后续位置”和“由显式模板参数填充的后续位置”应被认为是顺序位置,但这还很不清楚。如果是这样,这也令人困惑,因为有一系列与函数参数包相关联的参数类型,但它仍然是函数参数包。

[给出预期行为的另一种可能的实现方式是:当一个或多个显式模板参数T=unsigned int,...,A_1对应于模板参数包A_k时,发明另一个相同类型的模板参数包P,并用模板参数列表{More_P,...,PA_1替换A_k的每个扩展}。然后可以像其他模板参数包一样推导More_P...。如果从未推断出More_P,则在评估语义之前,将其所有扩展都用一个空列表替换所有其他推断的替换。但是标准中对此解释的理由更少。]

我是否错过了标准中更好地描述显式模板参数和推导的模板参数如何一起形成一个模板参数包的单个列表的内容?

1 个答案:

答案 0 :(得分:3)

它是[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
}
     

-示例]

相关问题