C++ 在编译时展开循环

Log*_*ick 3 c++ metaprogramming template-meta-programming

我的 C++ 项目中有几段代码,如下所示:

system<0>::global_instance.do_something();
system<1>::global_instance.do_something();
system<2>::global_instance.do_something();
system<3>::global_instance.do_something();
//...
system<29>::global_instance.do_something();
system<30>::global_instance.do_something();
system<31>::global_instance.do_something();
Run Code Online (Sandbox Code Playgroud)

好像有点重复吧?如果我能做这样的事情就太好了:

for(int i = 0; i < 32; i++)
{
  system<i>::global_instance.do_something();
}
Run Code Online (Sandbox Code Playgroud)

不幸的是,这行不通,因为 的值i在编译时未知,因此不能用作system.

我需要的是一种在编译时展开或展开循环的方法。

我已经看到一些使用模板来实现展开的实现,但它们不适用于我想要做的事情。理想情况下,循环展开将在预处理期间进行,并且可以将任意语句作为输入。

例如,这个:

#unroll NUM 0 5
foo<NUM>();
#endunroll
Run Code Online (Sandbox Code Playgroud)

会被翻译成这样:

foo<0>();
foo<1>();
foo<2>();
foo<3>();
foo<4>();
foo<5>();
Run Code Online (Sandbox Code Playgroud)

和这个:

#unroll NUM 0 5
blah blah blah NUM
#endunroll
Run Code Online (Sandbox Code Playgroud)

会被翻译成这样:

blah blah blah 0
blah blah blah 1
blah blah blah 2
blah blah blah 3
blah blah blah 4
blah blah blah 5
Run Code Online (Sandbox Code Playgroud)

即使blah blah blah NUM是语法上不正确的语句,它仍然应该被复制,并且标记 NUM 的每个实例都应该被适当的数字值替换。

有什么办法可以在 vanilla c++ 中实现这一点吗?或者是否有任何特殊的 C++ 编译器添加了此功能?

编辑:我在评论中找到了@HTNW 的答案。

#include <iostream>
using namespace std;

template<auto begin, auto end>
inline void unroll(auto f)
{
  if constexpr(begin < end)
  {
    f.template operator()<begin>();
    unroll<begin + 1, end>(f);
  }
}

template<int NUM>
struct thingy
{
  static void print(){cout << NUM << endl;}
};

int main()
{
  unroll<0,8>([]<int i>()
  {
    thingy<i>::print();
  });
}
Run Code Online (Sandbox Code Playgroud)

输出:

0
1
2
3
4
5
6
7
Run Code Online (Sandbox Code Playgroud)

使这项工作的行是f.template operator()<begin>();. 我不知道这条线是什么意思,我以前从未见过它。如果有人可以向我解释,那就太好了。

Pau*_*ers 5

您可以使用std::index_sequence折叠表达式,例如:

#include <iostream>
#include <utility>

template <size_t x>
void foo ()
{
    std::cout << x << " ";
}

template <size_t... indices>
void unroll (std::index_sequence <indices...>)
{
    (foo <indices> (), ...);
}

int main ()
{
    unroll (std::make_index_sequence <32> {});
}
Run Code Online (Sandbox Code Playgroud)

输出:

0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
Run Code Online (Sandbox Code Playgroud)

更新:根据 HTNW 的评论,在 C++20 中,您可以将模板 lambda 作为附加参数传递unroll以避免对调用进行硬编码foo

#include <iostream>
#include <utility>

template <size_t x>
void foo ()
{
    std::cout << x << " ";
}

template <size_t... indices>
void unroll (auto f, std::index_sequence <indices...>)
{
    (f.template operator () <indices> (), ...);
}

int main ()
{
    unroll ([] <size_t i> () { foo <i> (); }, std::make_index_sequence <32> {});
}
Run Code Online (Sandbox Code Playgroud)