【问题标题】: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 个参数优先考虑可变参数模板?

【问题讨论】:

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

标签: c++ c++11 templates overloading language-lawyer


【解决方案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)。并不总是很容易知道什么让其他人感到困惑(尤其是当我们已经在上下文中时)。
猜你喜欢
  • 2016-11-12
  • 1970-01-01
  • 2014-07-11
  • 1970-01-01
  • 1970-01-01
  • 2010-10-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多