在C++中模板化'for'循环?

RAC*_*RAC 23 c++ optimization templates metaprogramming

我在下面有一个带有运行时for循环的C++代码段,

for(int i = 0; i < I; i++)
  for (int j = 0; j < J; j++)
    A( row(i,j), column(i,j) ) = f(i,j);
Run Code Online (Sandbox Code Playgroud)

该片段被重复调用.循环边界'I'和'J'在编译时是已知的(I/J是2到10的顺序).我想以某种方式使用模板展开循环.主要的瓶颈是row()和column()以及f()函数.我想用使用row<i,j>::enum技巧在编译时评估的等效元程序替换它们.

我真正喜欢的是最终将循环解析为一系列语句的东西,例如:

A(12,37) = 0.5;
A(15,23) = 0.25;
A(14,45) = 0.25;
Run Code Online (Sandbox Code Playgroud)

但是我想这样做而不会破坏for- for结构太多.本着精神:

TEMPLATE_FOR<i,0,I>
  TEMPLATE_FOR<j,0,J>
     A( row<i,j>::value, column<i,j>::value ) = f<i,j>::value
Run Code Online (Sandbox Code Playgroud)

可以提升:: lambda(或其他东西)帮我创建吗?

T.E*_*.D. 12

一个好的编译器应该为你展开.例如,在gcc中使用-O2选项进行编译会打开循环展开.

如果您尝试自己手动完成,除非您仔细测量并真正知道自己在做什么,否则最终会导致代码变慢.例如,在您手动展开的情况下,您可能会阻止编译器进行循环交换或stripmine优化(在gcc文档中查找--floop-interchange和-floop-strip-mine )

  • 如果行和列函数那么复杂,那么你将很难在模板中实现它们.你不能只在编译时运行C++代码,你必须使用神秘的编译器错误消息作为调试器再次编写它.你最好运行另一个程序作为你的构建的一部分,它生成所有值并吐出一个带有数组的.h文件. (5认同)
  • 如果你使这些例程"内联",这可能会给优化器足够的额外信息来做到这一点. (2认同)
  • 只要完整的函数定义在调用站点可见,就没有理由不能内联它们. (2认同)

Jam*_*kin 7

这是直接执行此操作的方法:

template <int i, int j>
struct inner
{
  static void value()
  {
    A(row<i,j>::value, column<i,j>::value) = f<i,j>::value;
    inner<i, j+1>::value();
  }
};

template <int i> struct inner<i, J> { static void value() {} };

template <int i>
struct outer
{
  static void value()
  {
    inner<i, 0>::value();
    outer<i+1>::value();
  }
};

template <> struct outer<I> { static void value() {} };

void test()
{
  outer<0>::value();
}
Run Code Online (Sandbox Code Playgroud)

如有必要,您可以A作为参数传递给每个values.

这是一种使用可变参数模板的方法,不需要硬编码的I和J:

#include <utility>

template <int j, class Columns>
struct Inner;

template <class Columns, class Rows>
struct Outer;

template <int j, int... i>
struct Inner<j, std::index_sequence<i...>>
{
  static void value() { (A(column<i, j>::value, row<i, j>::value), ...); }
};


template <int... j, class Columns>
struct Outer<std::index_sequence<j...>, Columns>
{
  static void value() { (Inner<j, Columns>::value(), ...); }
};

template <int I, int J>
void expand()
{
  Outer<std::make_index_sequence<I>, std::make_index_sequence<J>>::value();
}

void test()
{
  expand<3, 5>();
}
Run Code Online (Sandbox Code Playgroud)

(生成程序集的代码段:https://godbolt.org/g/DlgmEl)


Cis*_*one 5

你可以使用Boost MPL.

循环展开的一个例子是在这个mpl :: for_each页面上.

for_each< range_c<int,0,10> >( value_printer() );
Run Code Online (Sandbox Code Playgroud)

它似乎并没有在编译时进行全部评估,但它可能是一个很好的起点.