【发布时间】:2019-09-03 00:39:42
【问题描述】:
在某些情况下,在编译时评估/展开 for 循环可能很有用/必要。例如,要遍历tuple 的元素,需要使用std::get<I>,它依赖于模板int 参数I,因此必须在编译时对其进行评估。
使用编译递归可以解决特定问题,例如讨论过的here、here,以及专门针对std::tuplehere。
不过,我对如何实现一个通用编译时for循环很感兴趣。
下面的c++17代码实现了这个想法
#include <utility>
#include <tuple>
#include <string>
#include <iostream>
template <int start, int end, template <int> class OperatorType, typename... Args>
void compile_time_for(Args... args)
{
if constexpr (start < end)
{
OperatorType<start>()(std::forward<Args>(args)...);
compile_time_for<start + 1, end, OperatorType>(std::forward<Args>(args)...);
}
}
template <int I>
struct print_tuple_i {
template <typename... U>
void operator()(const std::tuple<U...>& x) { std::cout << std::get<I>(x) << " "; }
};
int main()
{
std::tuple<int, int, std::string> x{1, 2, "hello"};
compile_time_for<0, 3, print_tuple_i>(x);
return 0;
}
虽然代码可以工作,但最好能够简单地为例程compile_time_for 提供一个模板函数,而不是在每次迭代时实例化一个模板类。
然而,像下面这样的代码不能在c++17中编译
#include <utility>
#include <tuple>
#include <string>
#include <iostream>
template <int start, int end, template <int, typename...> class F, typename... Args>
void compile_time_for(F f, Args... args)
{
if constexpr (start < end)
{
f<start>(std::forward<Args>(args)...);
compile_time_for<start + 1, end>(f, std::forward<Args>(args)...);
}
}
template <int I, typename... U>
void myprint(const std::tuple<U...>& x) { std::cout << std::get<I>(x) << " "; }
int main()
{
std::tuple<int, int, std::string> x{1, 2, "hello"};
compile_time_for<0, 3>(myprint, x);
return 0;
}
使用 gcc 7.3.0 和选项 std=c++17 第一个错误是
for2.cpp:7:25: error: ‘auto’ parameter not permitted in this context
void compile_time_for(F f, Args... args)
问题是:
- 有没有办法编写
compile_time_for使其接受模板函数作为其第一个参数? - 如果问题 1. 是肯定的,那么在第一个工作代码中是否存在开销,因为例程在每次循环迭代时都会创建一个
OperatorType<start>类型的对象? - 是否有计划在即将推出的
c++20中引入诸如编译时 for 循环之类的功能?
【问题讨论】:
-
如何使用
std::index_sequence和std::make_index_sequence? -
或
std::apply:std::apply([](const auto&...args) { ((std::cout << args << " "), ...); }, x);. -
@Jarod42
std::appy如果在每次循环迭代中要完成的操作取决于迭代本身(例如通过模板参数),则不会执行此工作 -
@francesco:您可以输入(详细?)
decltype(args)。通常,C++20 也允许在 lambda 中使用显式模板。 -
对于您的 3),P1306 (expansion statements) 是建议的循环编译时间。它仍有望包含在 C++20 中,但不能保证。
标签: c++ c++17 variadic-templates template-meta-programming c++20