使用模板元编程,constexpr或宏在编译时构建数据结构

时间:2017-11-14 15:08:09

标签: c++ arrays templates macros template-meta-programming

我想优化我正在编写的一个小程序/库,因为2周后我有点卡住了,现在想知道我脑子里的东西是否有可能就是这样。 (请保持温和,我没有太多的元编程经验。)

我的目标当然是让编译器完成某些计算,这样程序员 - 希望 - 只需要在程序中的某一点编辑代码,并让编译器“创建”所有样板。我确实有一个非常好的想法如何用宏来做我想要的东西,但是如果可能的话,我希望我能用模板做。

我的目标是: 假设我有一个使用程序员可以派生的类。在那里,他可以有多个传入和传出的数据类型,我想以某种方式注册,以便基类可以对它们进行操作。

class my_own_multiply : function_base {
  in<int> a;
  in<float> b;
  out<double> c;

  // ["..."] // other content of the class that actually does something but is irrelevant
  register_ins<a, b> ins_of_function; // example meta-function calls
  register_outs<c> outs_of_function;
}

我到目前为止的元代码是:(但它不是喷射工作/完成)

template <typename... Ts>
struct register_ins {
  const std::array<std::unique_ptr<in_type_erasured>, sizeof...(Ts)> ins;

  constexpr std::array<std::unique_ptr<in_type_erasured>, sizeof...(Ts)>
    build_ins_array() {
      std::array<std::unique_ptr<in_type_erasured>, sizeof...(Ts)> ins_build;
      for (unsigned int i = 0; i < sizeof...(Ts); ++i) {
        ins_build[i] = std::make_unique<in_type_erasured>();
      }
    return ins_build;
  }

  constexpr register_ins() : ins(build_ins_array()) {
  }

  template <typename T>
  T getValueOf(unsigned int in_nr) {
    return ins[in_nr]->getValue();
  }
};

正如您所看到的,我想用可变数量的ins调用我的元模板代码。 (程序员可以在其中添加许多他喜欢的内容,但它们不会在运行时更改,因此可以在编译时“烘焙”)

元代码应该是创建一个数组,它具有ins数量的长度并被初始化,以便每个字段指向my_own_multiply类中的原始in。基本上给他一个可索引的数据结构,总是有正确的大小。并且我可以从function_base类访问以使用所有ins用于某些函数,这些函数也是可迭代的,使我的方便。

现在我已经研究了如何做到这一点,但我现在感觉我可能不会真的被允许在编译时“创建”这个数组,这样我就可以继续使用ins和b是非静态和非常数,以便我可以改变它们。无论如何,从我这边他们不一定是const,但我的compliler似乎不喜欢他们是自由的。我唯一需要const的是带指针的数组。但是使用constexpr可能会“让”我使它们成为常量?

好的,我会澄清我没有得到的: 当我试图创建我的元填充结构的“实例”时,它失败了,因为它期望各种const,constexpr等等。但我不想要它们,因为我需要能够改变大多数这些变量。我只需要这个元素来在编译时创建一个正确大小的数组。但是我不想为了实现这一点而牺牲必须使一切变为静态和常量。在这些条款下,这甚至是可能的吗?

1 个答案:

答案 0 :(得分:0)

我没有得到你想到的所有东西(也是关于你的例子中的std::unique_ptr),但也许这会有所帮助:

从C ++ 14(或C ++ 11,但严格限制)开始,您可以编写constexpr函数,这些函数可以在编译时进行评估。作为一个先决条件(用简单的话说),所有参数都是由调用者传递的#34;必须是constexpr。如果你想强制编译器替换那个&#34; call&#34;通过编译时计算的结果,您必须将结果分配给constexpr

编写常用函数(仅添加constexpr)允许编写易于阅读的代码。此外,您可以对两者使用相同的代码:编译时计算和运行时计算。

C ++ 17示例(类似的事情在C ++ 14中是可能的,尽管std中的某些内容只是缺少constexpr限定符):

http://coliru.stacked-crooked.com/a/154e2dfcc41fb6c7

#include <cassert>

#include <array>

template<class T, std::size_t N>
constexpr std::array<T, N> multiply(
  const std::array<T, N>& a,
  const std::array<T, N>& b
) {
// may be evaluated in `constexpr` or in non-`constexpr` context

// ... in simple man's words this means:
// inside this function, `a` and `b` are not `constexpr`
// but the return can be used as `constexpr` if all arguments are `constexpr` for the "caller"
  std::array<T, N> ret{};
  for(size_t n=0; n<N; ++n) ret[n] = a[n] * b[n];
  return ret;
}

int main() {
  {// compile-time evaluation is possible if the input data is `constexpr`
    constexpr auto a = std::array{2, 4, 6};
    constexpr auto b = std::array{1, 2, 3};
    constexpr auto c = multiply(a, b);// assigning to a `constexpr` guarantees compile-time evaluation

    static_assert(c[0] == 2);
    static_assert(c[1] == 8);
    static_assert(c[2] == 18);
  }
  {// for run-time data, the same function can be used
    auto a = std::array{2, 4, 6};
    auto b = std::array{1, 2, 3};
    auto c = multiply(a, b);

    assert(c[0] == 2);
    assert(c[1] == 8);
    assert(c[2] == 18);
  }
  return 0;
}