【问题标题】:How can I create new style for creating loop?如何创建用于创建循环的新样式?
【发布时间】:2017-01-19 23:04:35
【问题描述】:

我想使用宏来更简单地创建我的源代码

这是我的代码

constexpr auto make_index_sequence_array(size_t i ,std::index_sequence<arg...>) {
    return std::array<size_t, sizeof...(arg)> {i+arg...};
}
#define RANGE(start , end) make_index_sequence_array(start,std::make_index_sequence<end-start>{})
#define ZeroTo(end) RANGE(0 , end)
#define Repeat(end) for(auto itr : RANGE(0 , end))

void main() {
    vector<int> a = { 1,2,3 };
    for (auto row : RANGE(0,a.size()))
        cout << std::setw(4) << a[row];
    cout << '\n';
}

我知道我可以使用:

void main() {
    vector<int> a = { 1,2,3 };
    for (auto itr: a)
        cout << std::setw(4) << itr;
    cout << '\n';
}

但这是一个简单的例子,我想在更多情况下使用这种风格。 编译时的错误是:

错误 C2975 '_Size':'std::make_index_sequence' 的模板参数无效,预期的编译时常量表达式

如何使用我的宏?甚至有可能吗?

【问题讨论】:

  • 我的强烈意见如下:使用宏来缩短源代码是一个非常糟糕的主意。它隐藏了您想要查看的内容,并使查找错误成为一件非常痛苦的事情。宏有用处,但这不是一个好用的。
  • 其实我想让它更简单
  • 对于这种特殊情况,您可以使用for (auto row : boost::irange(0, a.size()))
  • 首先,至少用括号将所有宏参数括起来,这样复杂的项目就不会导致它以意想不到的方式中断。
  • 如错误提示,a.size() 不是编译时常量。您不能将其用作模板参数。

标签: c++ c++11


【解决方案1】:

我的建议是你使用迭代器,我在下面写了一个例子。像这样的东西会在 C++ 中做到这一点

#include <iostream>

template <class T>
struct RangeIter {
    RangeIter(T from, T to, T curr ) :
    _from(from), _to(to), _curr(curr) {
    }

    T operator*() const { 
      return _curr;
    }

    T operator++() {
      ++_curr;
      return _curr;
    }

    bool operator==(const RangeIter & other) {
      assert(_from == other._from && _to == other._to);
      return _curr == other._curr;
    }

    bool operator!=(const RangeIter & other) {
      return !(_curr == other._curr);
    }
    T _from, _to, _curr;
  };

template <class T>
struct Range {
  Range(T from, T to) : _from(from), _to(to) {}

  RangeIter<T> begin() { return RangeIter<T>(_from, _to, _from); }
  RangeIter<T> end() { 
    return RangeIter<T>(_from, _to, _to); 
  }

  T _from, _to; 
};

template <class T>
Range<T> makeRange(T to, T from) {
  return Range<T>(to, from);
}

int main() {

    for (auto i : makeRange(0, 10)) {
       std::cout << i << std::endl;
    }
}

对于 C++17,您可以为开始和结束迭代器使用不同的类型并对此进行改进。你可以使用哨兵。你可以看这里:How the new range-based for loop in C++17 helps Ranges TS?

这里只有 C++-17 的解决方案:

#include <iostream>


template <class T>
struct RangeSentinel {
   RangeSentinel(T stopVal) : _stopVal(stopVal) {}

   T _stopVal;
};

template <class T>
struct RangeIter {
    RangeIter(T from, T to, T curr) :
    _from(from), _to(to), _curr(curr) {
    }

    T operator*() const { 
      return _curr;
    }

    T operator++() {
      ++_curr;
      return _curr;
    }

    bool operator==(const RangeSentinel<T> & other) {
      assert(_from == other._from && _to == other._to);
      return _curr == other._stopVal;
    }

    bool operator!=(const RangeSentinel<T> & other) {
      return !(_curr == other._stopVal);
    }
    T _from, _to, _curr;
  };



template <class T>
struct Range {
  Range(T from, T to) : _from(from), _to(to) {}

  RangeIter<T> begin() { return RangeIter<T>(_from, _to, _from); }
  RangeSentinel<T> end() { 
    return RangeSentinel<T>(_to); 
  }

  T _from, _to; 
};

template <class T>
Range<T> makeRange(T to, T from) {
  return Range<T>(to, from);
}

int main() {

    for (auto i : makeRange(0, 10)) {
       std::cout << i << std::endl;
    }
}

如您所见,在 C++17 解决方案中,我不需要再次存储 _from 和 _to 变量,因为哨兵是不同的类型。

【讨论】:

  • 检查代码 int main() { for (auto i : makeRange(2, 11)) { std::cout
  • 感谢@SamMokari 的反馈。我认为这不会改变我试图说明的要点。我不是在做功课,我是在指导。 :) 我认为你可以从那里开始合理地调整它。
猜你喜欢
  • 2013-07-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-10-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多