使用表达式模板编译时间数组索引 - constexpr?

时间:2016-12-30 13:33:32

标签: c++ c++11

我想生成一个这样的函数:

double apply_stencil(const double *u, const int i, const width,
                     const int *offset, const double *weight)
{
  double t=0;
  for(int j=0; j<width; j++)
    t += u[i+offset[j]] * weight[j];
  return t;
}

但我想确保widthoffset,甚至weight是编译/时间常量。

通过定义类型可以实现:

template <typename S>
double apply_stencil(const double *u, const int i)
{
  double t=0;
  for(int j=0; j<S::width; j++)
    t += u[i+S::offset[j]] * S::weight[j];
  return t;
}

// then:
struct Stencil {
  const static double weight[];
  const static int offset[];
  const static unsigned int width = 3;
};

const double Stencil::weight[] = {1.0, 2.0, 1.0};
const int Stencil::offset[]    = {-1, 0, 1};

但是,这不是很漂亮。我希望用户能够在他们的应用程序代码中指定Stencil,然后从我的头文件中调用我的apply_stencil函数(这实际上是对更复杂的东西的简化)。

理想情况下,我希望使用表达式模板指定事物,如下所示:

const Symbol u;
const Stencil<3> s (1*u[-1] + 2*u[0] + 1*u[1]);

使用此基础架构:

struct Accessor {
  int offset;
};

struct Symbol
{
  Accessor operator[](int i) const {
    return Accessor{i};
  }
};

struct WeightedTerm
{
  double weight;
  int offset;
  WeightedTerm()
    : weight(1), offset(0) {}

  WeightedTerm(double a, Accessor u)
    : weight(a), offset(u.offset) {}
};

WeightedTerm operator*(double a, Accessor u) {
  return WeightedTerm(a,u);
}

template <int n>
struct Sum
{
  WeightedTerm terms[n];

  Sum(WeightedTerm x, WeightedTerm y) {
    terms[0] = x;
    terms[1] = y;
  }

  Sum(Sum<n-1> x, WeightedTerm y) {
    for(int i = 0; i < n-1; ++i)
      terms[i] = x.terms[i];

    terms[n-1] = y;
  }
};

Sum<2> operator+(WeightedTerm x, WeightedTerm y) {
  return Sum<2>(x,y);
}

template <int n>
Sum<n+1> operator+(Sum<n> x, WeightedTerm y) {
  return Sum<n+1>(x,y);
}

template <int width>
struct Stencil
{
  double weights[width];
  int offsets[width];
  Stencil(const Sum<width> s) {

    for(int j = 0; j < width; ++j) {
      weights[j] = s.terms[j].weight;
      offsets[j] = s.terms[j].offset;
    }
  };
};

这看起来不错,但现在,数组不一定是已知的编译时间。如果我像上面那样用表达式中的文字来编写它,我已经验证了编译器可以进行正确的优化。但我想找到一种方法来保证它们总是编译时间常量。

我认为我可以将offset编码为AccessorWeightedTerm中的模板参数,但我无法看到我如何做到这一点并保留所需的表达式语法因为operator()将偏移量作为常规参数。

所以,问题是,是否有办法实现这一目标?我觉得constexpr可以在这里使用,我有点不熟悉。

1 个答案:

答案 0 :(得分:1)

使用std::integral_constant别名。如果您想要易用,请使用用户定义的文字。

constexpr int int_from_chars(){return 0;}

constexpr int k_pow(unsigned int x, unsigned int b=10){
  return (x==0)?1:(k_pow(x-1,b)*b);
}

template<class...Chars>
constexpr int int_from_chars(char x, Chars...chars){
  return k_pow(sizeof...(Chars))*(x-'0') + int_from_chars(chars...);
}
template<char...Cs>
constexpr auto operator""_kint(){
  return std::integral_constant<int, int_from_chars(Cs...)>{};
}
或某些人。需要抛光。

现在7_kint是编译时编码为7的类型。

您可以将integral_constant<int, K>作为函数参数,其中K是模板非类型参数。

延伸到八进制/十六进制/二进制作为练习。

template<int width>
double apply_stencil(const double *u, const int i, std::integral_constant<int,width>,
                 const int *offset, const double *weight)
{
  double t=0;
  for(int j=0; j<width; j++)
    t += u[i+offset[j]] * weight[j];
  return t;
}

通过

调用
apply_stencil( ptr, i, 7_kint, poffsets, pweights);

可以使用类似(但更复杂)的技术来传递偏移量。重量是一团糟,因为很少有编译时双重支持。