【问题标题】:Compile time evaluation of a C++ loopC++ 循环的编译时评估
【发布时间】:2015-12-02 06:52:08
【问题描述】:

我正在尝试编写一个我想在编译时评估的 C++ 循环。这是因为我需要使用循环变量作为模板参数来初始化一个类。在非常简单的情况下,它看起来像:

for (unsigned int i = 1; i < 8; i++) {
  Vector<i> foo;
  // do something with foo
}

在经历了一些类似的 StackOverflow questions 之后,我找到了一种递归编写静态 for 循环的方法。现在我的代码看起来像这样:

template <unsigned int i, unsigned int end>
struct static_for {
  template <typename Lambda>
  void operator()(const Lambda& function) const {
    if (i < end) {
      function(i);
      static_for<start + 1, end>(function);
    }
  }
};

template <int N>
struct static_for<N, N> {
  template <typename Lambda>
  void operator()(const Lambda& function) const {
    // This is just to avoid the unused variable warning - does nothing.
    (void)function;
  }
};

这完全符合预期。为了调用这个static_for循环,我可以简单地写:

static_for<0, 8>()([](int size) {
      // some code here.
    });

然而,问题是我仍然无法在编译时评估i。正如我无法在 lambda 表达式中创建模板化对象:

static_for<0, 8>()([](int size) {
      Vector<size> var; // does not compile as size is not a constexpr.
    });

我怎样才能做到这一点?我的static_for 循环将i 变量作为模板参数,它在编译时可用。我希望我的 lambda 表达式也在编译时接收这个参数 (size),而不是作为运行时变量传入。这将如何工作?从概念上讲,这作为一种更通用的语言功能似乎也很简单且有用。

【问题讨论】:

  • 问题是 lambda 不是 constexpr 表达式。如果您改用 constexpr 函数,它可能会起作用。
  • 问题在于将 constexpr 函数模板作为可调用对象传递。知道代码的用途吗?
  • 您可以尝试将 std::integral_constant 传递给 lambda 而不是 int。使用带有“auto”的通用 C++14 lambda

标签: c++ lambda compilation


【解决方案1】:

如果您可以使用 C++14,则可以将 std::integral_constant 传递给通用 lambda,而不是 intgcc.godbolt.org example

template <unsigned int i, unsigned int end>
struct static_for {
  template <typename Lambda>
  void operator()(const Lambda& function) const {
    if (i < end) {
      function(std::integral_constant<int, i>{});
      static_for<start + 1, end>(function);
    }
  }
};

static_for<0, 8>()([](auto size) {
  Vector<decltype(size)::value> var; // does not compile as size is not a constexpr.
});

【讨论】:

  • 那肯定行得通!不幸的是 C++11 不支持它。对于我的工作项目,我坚持使用 C++11。有什么技巧可以让它在那里工作吗?
  • 您可以使用带有模板化 operator() 的可调用对象而不是 lambda。
  • Here is a C++11 example。抱歉格式不好,我在机场的平板电脑上打字:)
【解决方案2】:

我根据@Vittorio Romero 的回答做了一些不同的解决方案,它应该适用于 c++11 和 g++ 4.9。你可以运行它here。在@Vittorio 的解决方案中,由于integral_constant(参见cmets 中的示例代码)的使用,您必须在块范围之外定义Lambda 函数。在我的解决方案中,您有更多类似 lambda 的行为。您可以在其他函数中定义您的结构并向其传递任意数量的参数。

[编辑] 好吧,我的思维工作有点太晚了;)不幸的是,我无法提出一种在编译时工作的 lambda-like 解决方案。所以你必须在块范围之外定义你的 lambda 结构。你可以运行修改后的代码here

#include <type_traits>
#include <iostream>

template <int COUNT, int MAX, typename Lambda>
struct StaticFor 
{
    template <typename... Args>
    static void impl(Args&&... args) 
    {
        Lambda::impl(std::integral_constant<int, COUNT>(), std::forward<Args>(args)...);
        StaticFor<COUNT + 1, MAX, Lambda>::impl(std::forward<Args>(args)...);
    }
};

template <int N, typename Lambda>
struct StaticFor<N, N, Lambda> 
{
    template <typename... Args>
    static void impl(Args&&... args) 
    {
    }
};

static const int DIM = 3;
static const int POINTS = 6;

template <int m, typename T>
T foo(T value)
{ 
    return value * m;
}

struct IterateDim
{
  template <typename D, typename P>    
  static void impl(D dIdx, P pIdx, float (&smpl)[POINTS][DIM])
  {
    smpl[P::value][D::value] = foo<D::value>(P::value);
  }
};

struct IteratePoints
{
  template <typename P>  
  static void impl(P pIdx, float (&smpl)[POINTS][DIM])
  {
    StaticFor<0, DIM, IterateDim>::impl(pIdx, smpl);
  }
};

int main(void)
{
  float table[POINTS][DIM];

 StaticFor<0, POINTS, IteratePoints>::impl(table);

  for (int p = 0; p < POINTS; ++p)
  {
    for (int d = 0; d < DIM; ++d)
    {
      std::cout << table[p][d] << ", ";
    }
  }
}

【讨论】:

    猜你喜欢
    • 2011-09-27
    • 2011-12-17
    • 1970-01-01
    • 2012-02-01
    • 2021-02-13
    • 1970-01-01
    • 2018-06-23
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多