折叠表达式与编译递归

时间:2019-04-16 09:46:09

标签: c++ c++11 c++17 sfinae fold-expression

c++17中,我们具有fold表达式,该表达式可以大大简化可以使用编译器递归和SFINAE或重载实现的代码。 例如,在以下代码中

#include <iostream>
#include <utility>

template<typename ...Args>
void printer(Args&&... args) {
  (std::cout << ... << args) << '\n';
}

void printer_cpp11() { }

template <typename First, typename ...Args>
void printer_cpp11(First&& first, Args&&... args)
{
  std::cout << first;
  printer_cpp11(std::forward<Args>(args)...);
}

int main()
{
  printer(3, 4, "hello");

  std::cout << std::endl;

  printer_cpp11(3, 4, "hello");

  return 0;
}

c++17函数printer(取自cpp reference)执行的功能与其c++11版本printer_cpp11完全相同。

在编译时,会生成函数printer_cpp11的多个重载,而需要使用折叠表达式的单个函数printer

c++11样式相比,使用折叠表达式在性能方面是否有优势?还是可以假设编译器内联printer_cpp11的所有重载,从而创建性能相同的代码?

3 个答案:

答案 0 :(得分:1)

由于内联,两个版本都将导致相同的代码生成,因此运行时性能完全相同:https://gcc.godbolt.org/z/VIHTvZ(清除流混乱的代码)。

但是,与递归实例化相比,使用fold表达式预期编译时间和内存使用情况会更好,因此,通常首选fold表达式。更不用说它们还提供了更简洁的代码推理方式。

答案 1 :(得分:1)

只需添加到@SergeyA的答案中,您就可以通过执行以下操作来减轻c ++ 11版本中对递归和空函数的需求:

template <typename ...Args>
void printer_cpp11_norecursion( Args&&... args)
{
  using do_ = int[];
  do_{0,
    (std::cout << args,0)...
  };
}

应该在c ++ 11上生成与其他两个版本(https://gcc.godbolt.org/z/hyAyiz)相同的结果,并且可能具有更好的编译时间。

答案 2 :(得分:0)

编译器将为每个具有不同参数的调用创建 printer 的新实例,并在函数中展开 operator <<< / strong>调用:

https://godbolt.org/z/Zz9Ik9

您还可以查看此处发生的情况:https://cppinsights.io/

但是最终-测量将揭示它是否会带来性能提升。