【问题标题】:print a list using variadic templates使用可变参数模板打印列表
【发布时间】:2021-05-19 04:55:57
【问题描述】:

我需要您的帮助来找出以下代码无法编译的原因。

#include <iostream>

template <int I>
void foo(){
  std::cout << I << std::endl;
  std::cout << "end of list" << std::endl;
}

template <int I, int ... Ints>
void foo(){
  std::cout << I << std::endl;
  foo<Ints...>();
}


int main(){
  foo<1, 2>();
  return 0;
}

我收到此错误。

function_parameter_pack.cpp: In instantiation of ‘void foo() [with int I = 1; int ...Ints = {2}]’:
function_parameter_pack.cpp:17:13:   required from here
function_parameter_pack.cpp:12:15: error: call of overloaded ‘foo<2>()’ is ambiguous
   foo<Ints...>();
   ~~~~~~~~~~~~^~
function_parameter_pack.cpp:4:6: note: candidate: void foo() [with int I = 2]
 void foo(){
      ^~~
function_parameter_pack.cpp:10:6: note: candidate: void foo() [with int I = 2; int ...Ints = {}]
 void foo(){

不是应该选择更专业的功能吗?即只有一个模板(第一个)。

【问题讨论】:

标签: c++ c++11 recursion variadic-templates


【解决方案1】:

foo&lt;2&gt; 可以很好地匹配两个模板(因为 int... 也匹配零 ints),这就是编译器抱怨歧义的原因。

不是应该选择更专业的功能吗?

是的,但是这些同样特别。通过查看函数的参数而不是模板参数来推断哪个是更专业的匹配函数。

解决这个问题的一种方法是让第二个函数模板采用2个或更多模板参数:

#include <iostream>

template<int I>
void foo() {
    std::cout << I << std::endl;
    std::cout << "end of list" << std::endl;
}

template<int I, int J, int... Ints>
void foo() {
    std::cout << I << std::endl;
    foo<J, Ints...>();
}

int main() {
    foo<1>();
    foo<1, 2>();
    foo<1, 2, 3>();
}

【讨论】:

  • 请注意,尽管template &lt;typename T&gt; void f(T); template &lt;typename T, typename ...Ts&gt; void f(T, Ts...);f(42); 会编译,但原因是(过度)简化了,即使f&lt;int&gt; 匹配两个模板。
  • @Jarod42 是的。我知道那部分太糟糕了,无法表达为什么会这样。关于我猜的问题中的非类型模板参数?如果您提出答案来解释它,我会很高兴地投票。 :-)
  • 重载解析有很多规则,作为我的第一条评论,""更专业"是通过参数完成的,而不是模板参数"。因此,与类型/非类型模板参数无关。
  • @Jarod42 啊,我没注意到那个评论。谢谢。我把它放在答案中,我试着用我自己的话。我希望我没有搞砸。
  • 我的函数使用参数/参数(除了模板参数)。
【解决方案2】:

我更喜欢使用虚拟数组的解决方案,所以它的工作原理类似于折叠表达式。

template<int... Ints>
void foo() {
    char dummy[] {((std::cout << Ints << ','), '0')...};
    std::cout << '\n';
}

https://godbolt.org/z/1Y1noK

【讨论】:

  • Regular for range 在这里可以解决问题for (int i : {Is...}) :-)
  • 为什么不只是折叠表达式?它具有相同的排序保证。
  • 折叠表达式在 C++17 中,问题用 C++11 标记
  • 抱歉,没注意到。
【解决方案3】:

解决问题的另一种方法是使用不做任何事情的终端递归函数,它接受不同于值(可能是类型名)但具有默认值的东西,

template <typename = void>
void foo() {
  std::cout << "end of list" << std::endl;
}

Ints... 为空时拦截foo&lt;Ints...&gt;() 调用。

递归情况,拦截每个模板整数值,保持不变

template <int I, int ... Ints>
void foo(){
  std::cout << I << std::endl;
  foo<Ints...>();
}

【讨论】:

  • 另一种方法是摆脱递归:template &lt;int ... Is&gt; void foo() { for (int i : {Is...}) {std::cout &lt;&lt; i &lt;&lt; std::endl;} std::cout &lt;&lt; "end of list\n";} :-)(或 C++17 折叠表达式 ((std::cout &lt;&lt; Is &lt;&lt; std::endl), ...);(或 C++11 的等效技巧))。
  • @Jarod42 - 我知道......并且最好避免递归......但我更多地解释这个问题是关于递归管理而不是函数体的执行。
猜你喜欢
  • 2013-06-04
  • 1970-01-01
  • 2022-01-14
  • 2012-09-02
  • 2014-04-21
  • 2011-11-06
  • 2021-05-23
  • 2022-01-06
  • 2021-10-01
相关资源
最近更新 更多