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>();. 我不知道这条线是什么意思,我以前从未见过它。如果有人可以向我解释,那就太好了。
您可以使用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)