【问题标题】:Integer ranges in C++ - what should I do while the standard's not there yet?C++ 中的整数范围 - 当标准还没有出现时我应该怎么做?
【发布时间】:2016-03-11 10:22:06
【问题描述】:

我知道 C++ 重量级人物正在努力将范围纳入语言,或者至少是标准库:

公平地说 - 我还没有阅读官方建议。我只是一个想要使用简单范围功能的谦虚 C++ 程序员。我应该今天做什么,而不是在 C++17 之后使用,比如说,具有步幅的简单整数范围?当然,当我开始实际使用范围时,我的要求可能会扩大,但我仍然不追求更高级的东西和各种极端情况。我想。

在更严格的方面,由于兼容性需要 (CUDA),我正在使用一个有点旧的环境,所以我需要一些可以与 GCC 4.9.3 及其相应的标准库版本一起使用的东西,以及我可以在其中使用的东西GPU 代码(不一定是相同的库/头文件,但希望相同)。

好吧,我应该推出自己的功能有限的整数范围类型,等待手续解决 - 还是应该选择更重量级的替代品之一(Niebler 的“Ranges v3”、Boost irange 等) ?

【问题讨论】:

  • 这取决于你想要做什么。如果您只需要一个范围 boost.range 可能就足够了;如果您想要完整的功能,您可以使用 eric niebler 编写的范围库;它在 github 上。
  • @KlemensMorgenstern:Boost 和 CUDA 不能很好地结合在一起,我需要从 Boost 中“提取”irange/range。做起来可能难,也可能不难。
  • 这是一个只有头文件的库,所以去吧。我不知道 CUDA,但 boost.range 是符合 C++ 代码的。
  • 整数范围是什么意思:你的意思是[1,2,3,4,5,6)[1,3,5,7)的范围? (即,它们之间具有固定步幅的整数范围)。还是别的什么?
  • @Yakk:我的意思是for(auto i = i_start; i < i_end; i += i_stride) { ... }

标签: c++ c++11 boost c++17


【解决方案1】:

编写一个简单的“索引”——一个存储类型T并通过复制返回类型O=T的输入迭代器——很容易。

我所说的index 是一个迭代器,它对支持前进和比较的事物(包括其他迭代器和类似整数的事物)进行迭代,并在取消引用时返回所包含对象的副本。生成 range-over-iterators-into-ranges 也很有用。

template<class T, class O=T>
struct index_t:
  std::iterator<
    std::input_iterator_tag,
    O, std::ptrdiff_t, O*, O
  >
{
  T t;
  explicit index_t(T&& tin):t(std::move(tin)){}
  explicit index_t(T const& tin):t(tin){}
  index_t()=default;
  index_t(index_t&&)=default;
  index_t(index_t const&)=default;
  index_t& operator=(index_t&&)=default;
  index_t& operator=(index_t const&)=default;

  O operator*()const{ return t; }
  O operator*(){ return t; }
  O const* operator->()const{ return &t; }
  O* operator->(){ return &t; }

  index_t& operator++(){ ++t; return *this; }
  index_t operator++(int){ auto r = *this; ++*this; return r; }

  friend bool operator==(index_t const& lhs, index_t const& rhs) {
    return lhs.t == rhs.t;
  }
  friend bool operator!=(index_t const& lhs, index_t const& rhs) { return !(lhs==rhs); }
};
template<class Scalar>
index_t<Scalar> index_to( Scalar s ) {
  return index_t<Scalar>(std::move(s));
}

或类似的。 index_t&lt;int&gt; 是一个无步长的整数迭代器。

如果我们想要跨步:

template<class T, class D=std::size_t>
struct strided {
  T t;
  D stride;
  strided( T tin, D sin ):t(std::move(tin)), stride(std::move(sin)) {}

  strided& operator++(){ t+=stride; return *this; }
  strided operator++(int){ auto r = *this; ++*this; return r; }
  operator T()&&{return std::move(t);}
  operator T() const &{return t;}

  friend bool operator==(strided const& lhs, strided const& rhs){
    return lhs.t == rhs.t;
  }
  friend bool operator!=(strided const& lhs, strided const& rhs) { return !(lhs==rhs); }
};

template<class T, class D=std::size_t>
using strided_index_t = index_t<strided<T,D>, T>;

现在你只需要写range_t&lt;Iterator&gt;(开始、结束、空、前、后、构造函数)。

template<class T>
strided_index_t<T> strided_index_to( T t, std::size_t d ) {
  return strided_index_t<T>( {std::move(t), d} );
}
template<class It>
struct range_t {
  It b,e;
  using element_type = decltype( *std::declval<It>() );
  It begin() const { return b; }
  It end() const { return e; }
  bool empty() const { return begin()==end(); }
  element_type front() const { return *begin(); }
  element_type back() const { return *std::prev(end()); }
};
template<class It>
range_t<It> range(It b, It e){
  return {std::move(b), std::move(e)};
}

现在我们可以设置索引范围了:

template<class Scalar>
range_t<index_t<Scalar>> index_range( Scalar b, Scalar e ) {
  return range( index_to(std::move(b)), index_to(std::move(e)) );
}
template<class Scalar>
range_t<strided_index_t<Scalar>> strided_index_range( Scalar b, Scalar e, std::size_t d ) {
  return range( strided_index_to(std::move(b), d), strided_index_to(std::move(e), d) );
}

现在我们测试:

for (int i : index_range(0, 10))
  std::cout << i << '\n';
for (int i : strided_index_range(0, 10, 2))
  std::cout << i << '\n';

boost 和其他基于范围的库中也存在类似的对象。

Live example

顺便说一句,

for( auto it : index_to( begin(vec), end(vec) ) )

以基于范围的方式迭代vec 的迭代器。写作

for( auto it : iterators_into(vec) )

也很简单。

【讨论】:

  • std::iterator 不会从标准中删除吗?
  • @einpoklum 你为什么这么认为?它只是一种帮助类型,使定义有效的迭代器变得更容易......
  • 我想我在某种 C++17 状态更新中读到过它。没关系。另一点:由于 D 保存在变量中,与 for 循环相比,我不会冒更多的赋值和读取值的风险吗?
  • @einpoklum 然后写template&lt;class T, T stride&gt; class fixed_stride 硬编码步幅,如果你想要硬编码的编译时步幅?如果您没有硬编码的步幅,但 stride 显然未更改,则可以将 stride 的状态内联到本地并确定为不变并由半体面的编译器优化。
  • 我们拭目以待。谢谢。
猜你喜欢
  • 2018-11-30
  • 1970-01-01
  • 1970-01-01
  • 2010-10-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-10-09
相关资源
最近更新 更多