可变参数模板和省略号有啥区别?

Posted

技术标签:

【中文标题】可变参数模板和省略号有啥区别?【英文标题】:What is the difference between variadic template and ellipsis?可变参数模板和省略号有什么区别? 【发布时间】:2020-09-09 05:56:11 【问题描述】:

我知道标题没有多大意义,但代码会解释我的问题。

template<typename T>
void foo(T...) std::cout << 'A';

template<typename... Ts>
void foo(Ts...) std::cout << 'B';

int main()  
   foo(1); 
   foo(1,2);

在阅读续篇之前尝试猜测这个程序的输出:

所以输出是AB

谁能解释为什么 1 个参数函数优先考虑省略号,而 2 个参数优先考虑可变参数模板?

【问题讨论】:

你确定第一个函数模板编译了吗? @IgorR。 gcc.godbolt.org/z/67Dgk8 @Ayxan 我会说这是一个奇怪的 gcc 错误,但 clang 也可以编译它......令人兴奋。 template&lt;typename... Ts&gt; void foo(Ts......) 更“有趣”,请阅读 foo(Ts..., ...) @Jarod42 现在只需要解释为什么第一次调用选择了这个重载。 【参考方案1】:

什么是第一个重载?

每[dcl.fct]

语法正确且“...”不属于 抽象声明符,“, ...”与“...”同义。

因此,这使得第一个重载成为可变参数函数(也恰好是模板化的),相当于:

template<typename T>
void foo(T, ...) std::cout << 'A';

(注意,cppreference 页面包含一个示例,其中第一个参数和可变参数之间的逗号省略了类似的省略号。)

为什么我们会看到特定的输出?

当您传递两个参数时,编译器更喜欢其他重载,因为在重载解析期间,在对可行重载进行排序时,省略号转换序列总是排在最后。 ([over.ics.rank])

当传递单个参数时,编译器更喜欢第一个重载,因为简单来说,省略号不匹配(因为没有什么可以匹配)。这可以防止该函数被视为省略号转换序列。然后进行正常函数模板排名,并确定此函数比可变参数更专业([temp.deduct.partial])

【讨论】:

请注意,您可以引用一个特定的句子,这里可能更方便:dcl.fct/4/8。 @DanielLangr:谢谢我不知道 是的,它是一个可变参数函数,但 OP 已经知道这一点。他在问为什么第二个调用使用可变参数函数模板而不是 C 风格的可变参数函数。 @Sebastian Redl 可变参数函数总是最差的匹配。所以问题可能是为什么第一个调用使用它。 它确实在没有第一个函数gcc.godbolt.org/z/bhsmzb的情况下编译【参考方案2】:

关注overload_resolution#Best_viable_function

对于f(1)

我们通过 5)

或者,如果不是这样,F1 和 F2 都是模板特化,根据模板特化的偏序规则,F1 更加特化

来自Function_template_overloading

在两个方向上考虑每个 P 和 A 之后,如果对于所考虑的每种类型, [..]

在平局的情况下,如果一个函数模板有一个尾随参数包而另一个没有,则认为带有省略参数的模板比带有空参数包的模板更专业。

因此省略号函数template&lt;typename T&gt; void foo(T,...) 被认为比可变参数template&lt;typename ...Ts&gt; void foo(Ts...) 更专业。

f(1, 2)

阅读Ranking_of_implicit_conversion_sequences

在可变参数版本中我们有完全匹配,而省略号有省略号转换序列

所以可变参数更好匹配。

【讨论】:

虽然这确实解释了重载解决方案 (+1),但我认为为了完整起见,您还应该提及为什么第一个函数是 C 风格的可变参数函数。 OP 似乎没有意识到这一点。 @Ayxan:我没有看到任何来自 OP 的混淆。有一些评论员虽然 @Ayxan:我会说混淆而不是混淆。 什么样的人会以其他方式混淆他们需要帮助的代码,然后不提?关键是让别人更难帮助你?或者我需要更多的咖啡。 OP 可能用于以这种方式使用省略号。与运算符优先级一样,一些被认为是已知的,然后添加括号可能被认为是多余的,只是增加冗长(3 + (4 * 5)(a + b) &lt; c),因为其他省略“额外”括号是不安全和混淆的(a || b1 &amp;&amp; b2)。并不总是很容易知道什么让其他人感到困惑(尤其是当我们已经在上下文中时)。

以上是关于可变参数模板和省略号有啥区别?的主要内容,如果未能解决你的问题,请参考以下文章

[C++11 模板的改进] --- 可变参数模板

可变参数模板

第20课 可变参数模板_模板参数包和函数参数包

C++中的可变参数模板

第21课 可变参数模板_展开参数包

可变参数省略号使用简介