折叠表达式 vs 编译递归
Fold expression vs compile recursion
在 c++17
中,我们有折叠表达式,它可以大大简化原本可以使用编译器递归和 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
的所有重载,从而创建具有同等性能的代码?
编译器将为每个使用不同参数的调用创建一个新的 printer 实例,并在函数中展开 operator<<来电:
你也可以在这里看到发生了什么:https://cppinsights.io/
但最终 - 测量将揭示它是否会带来性能提升。
由于内联,两个版本将导致相同的代码生成,因此 运行 时间性能完全相同:https://gcc.godbolt.org/z/VIHTvZ(代码流杂乱)。
但是,折叠表达式的编译时间和内存使用预计比递归实例化要好得多,因此,折叠表达式通常是首选。更不用说它们还提供了更清晰、更易于推理的代码。
只需添加到@SergeyA 的答案中,您就可以通过执行以下操作来减轻 c++11 版本中对递归和空函数的需求...
template <typename ...Args>
void printer_cpp11_norecursion( Args&&... args)
{
using do_ = int[];
do_{0,
(std::cout << args,0)...
};
}
它应该生成与其他两个版本 (https://gcc.godbolt.org/z/hyAyiz) 相同的结果,并且在 c++11 上的编译时间可能更好。
在 c++17
中,我们有折叠表达式,它可以大大简化原本可以使用编译器递归和 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
的所有重载,从而创建具有同等性能的代码?
编译器将为每个使用不同参数的调用创建一个新的 printer 实例,并在函数中展开 operator<<来电:
你也可以在这里看到发生了什么:https://cppinsights.io/
但最终 - 测量将揭示它是否会带来性能提升。
由于内联,两个版本将导致相同的代码生成,因此 运行 时间性能完全相同:https://gcc.godbolt.org/z/VIHTvZ(代码流杂乱)。
但是,折叠表达式的编译时间和内存使用预计比递归实例化要好得多,因此,折叠表达式通常是首选。更不用说它们还提供了更清晰、更易于推理的代码。
只需添加到@SergeyA 的答案中,您就可以通过执行以下操作来减轻 c++11 版本中对递归和空函数的需求...
template <typename ...Args>
void printer_cpp11_norecursion( Args&&... args)
{
using do_ = int[];
do_{0,
(std::cout << args,0)...
};
}
它应该生成与其他两个版本 (https://gcc.godbolt.org/z/hyAyiz) 相同的结果,并且在 c++11 上的编译时间可能更好。