使循环变量在C ++

时间:2017-11-04 14:51:30

标签: c++ c++11 templates template-meta-programming constexpr

我目前正在为高级综合创建算术运算库。

为此,我还创建了一个库来操作位和位向量,就像在VHDL中完成一样。为了使我的库可以合成,几乎所有东西都必须在编译时解决。

但是,我遇到了循环问题。

的确,我希望能够写出类似的东西:

const int N = 5;
for(int i = 0; i < N-2; i++) {
    x.bit<i+2>() = x.bit<i>();
}

当然,它不会编译,因为i是一个变量而不是在编译时确定的常量。

但是,N是常量,此代码严格等同于:

x.bit<2>() = x.bit<0>();
x.bit<3>() = x.bit<1>();
x.bit<4>() = x.bit<2>();

编译并完美地运作。

有没有办法让编译器(在我的情况下为gcc)展开循环,因为N是常量?或者定义一个宏或constexpr,它可以用干净的语法来做到这一点?这相当于VHDL中生成

3 个答案:

答案 0 :(得分:3)

虽然constexpr在C ++ 14/17中功能更强大,但还不可能将这种编译时/模板代码与普通循环混合使用。有一些关于引入可能在未来版本的C ++中启用它的构造的讨论。现在你有几个选择,要么递归调用带有整数模板参数的函数,要么可能更简单,在这种情况下是C ++ 17 fold expression。你也可以使用C ++ 11可变参数模板扩展来获得类似的结果来折叠表达式,尽管折叠表达式更强大。

刚刚看到你关于被C ++ 11困住的评论,你认为使用递归函数方法可能会更好。我已经在示例中添加了这种方法。

如果您能够使用C ++ 14,您可能还需要考虑完全移入constexpr函数/类型的土地,这样您的bit<I>()函数就不会被模板化,而只会是{{1} 1}}函数constexpr。然后,您可以使用普通函数和循环。鉴于对bit(i)函数的C ++ 11限制,在您的情况下可能不太有用。我已经使用这种方法添加了一个示例。

constexpr

答案 1 :(得分:2)

也没有std::integer_sequence(但我建议实现替代并使用它),在C ++ 11中你可以使用模板部分特化。

我的意思是你可以实现像

这样的东西
template <int I, int Sh, int N>
struct shiftVal
 {
   template <typename T>
   static int func (T & t)
    { return t.template bit<I+Sh>() = t.template bit<I>(),
             shiftVal<I+1, Sh, N>::func(t); }
 };

template <int I, int Sh>
struct shiftVal<I, Sh, I>
 {
   template <typename T>
   static int func (T &)
    { return 0; }
 };

并且您的周期变为

shiftVal<0, 2, N-2>::func(x);

以下是一个完整的工作示例

#include <array>
#include <iostream>

template <std::size_t N>
struct foo
 {
   std::array<int, N> arr;

   template <int I>
   int & bit ()
    { return arr[I]; }
 };


template <int I, int Sh, int N>
struct shiftVal
 {
   template <typename T>
   static int func (T & t)
    { return t.template bit<I+Sh>() = t.template bit<I>(),
             shiftVal<I+1, Sh, N>::func(t); }
 };

template <int I, int Sh>
struct shiftVal<I, Sh, I>
 {
   template <typename T>
   static int func (T &)
    { return 0; }
 };

int main ()
 {
   foo<10U>  f { { { 2, 3, 5, 7, 11, 13, 17, 19, 23, 29 } } };

   for ( auto const & i : f.arr )
      std::cout << i << ' ';

   std::cout << std::endl;

   shiftVal<0, 2, 10-2>::func(f);

   for ( auto const & i : f.arr )
      std::cout << i << ' ';

   std::cout << std::endl;
 }

答案 2 :(得分:0)

没有其他人根据std::integer_sequence的C ++ 11模拟生成一个例子(正如WF,Passer By和Sopel以及更简单的解决方案IMHO所建议的那样)所以我提出了以下一个({{1}在现实中{}和std::index_sequence:模拟std::make_index_sequence更复杂)

std::integer_sequence

因此,用于重现被询问循环的函数(带有函数助手)可以写成

模板

template <std::size_t ...>
struct indexSequence
 { };

template <std::size_t N, std::size_t ... Next>
struct indexSequenceHelper : public indexSequenceHelper<N-1U, N-1U, Next...>
 { };

template <std::size_t ... Next>
struct indexSequenceHelper<0U, Next ... >
 { using type = indexSequence<Next ... >; };

template <std::size_t N>
using makeIndexSequence = typename indexSequenceHelper<N>::type;

并且跟随广告

void shiftValHelper (T & t, indexSequence<Is...> const &)
 {
   using unused = int[];

   (void)unused { 0,
      (t.template bit<Is+Sh>() = t.template bit<Is>(), 0)... };
 }


template <std::size_t Sh, std::size_t N, typename T>
void shiftVal (T & t)
 { shiftValHelper<Sh>(t, makeIndexSequence<N>{}); }

以下是一个完整的工作示例

shiftVal<2, N-2>(x);