c++ 范围的排序视图 - 如何创建 const_iterator?
c++ sorted view of range - how to create const_iterator?
我正在尝试编写一个 class,它应该作为一些基本元素序列的排序视图。到目前为止,我想出了一个非 const
版本。现在我在调整它以提供 const_iterator
功能时遇到了问题。
我目前的代码如下所示:
// forward declare iterator
template <class InputIt>
class sorted_range_iter;
template <class InputIt>
class sorted_range {
friend class sorted_range_iter<InputIt>;
private:
using T = typename InputIt::value_type;
InputIt _first;
InputIt _last;
std::vector<size_t> _indices;
public:
using iterator = sorted_range_iter<InputIt>;
sorted_range() = default;
sorted_range(InputIt first, InputIt last)
: _first(first), _last(last), _indices(std::distance(_first, _last)) {
std::iota(_indices.begin(), _indices.end(), 0);
};
template <class Compare = std::less<T>>
void sort(Compare comp = Compare()) {
std::sort(_indices.begin(), _indices.end(),
[this, &comp](size_t i1, size_t i2) {
return comp(*(_first + i1), *(_first + i2));
});
}
size_t size() const { return _indices.size(); }
T& operator[](size_t pos) { return *(_first + _indices[pos]); }
const T& operator[](size_t pos) const { return (*this)[pos]; }
iterator begin() { return iterator(0, this); }
iterator end() { return iterator(size(), this); }
};
相应的迭代器如下所示:
template <class InputIt>
class sorted_range_iter
: public std::iterator<std::forward_iterator_tag, InputIt> {
friend class sorted_range<InputIt>;
private:
using T = typename InputIt::value_type;
size_t _index;
sorted_range<InputIt>* _range;
sorted_range_iter(size_t index, sorted_range<InputIt>* range)
: _index(index), _range(range) {}
public:
T& operator*() { return *(_range->_first + _range->_indices[_index]); }
// pre-increment
const sorted_range_iter<InputIt>& operator++() {
_index++;
return *this;
}
// post-increment
sorted_range_iter<InputIt> operator++(int) {
sorted_range_iter<InputIt> result = *this;
++(*this);
return result;
}
bool operator!=(const sorted_range_iter<InputIt>& other) const {
return _index != other._index;
}
};
用法示例如下所示:
std::vector<int> t{5, 2, 3, 4};
auto rit = ref.begin();
sorted_range<std::vector<int>::iterator> r(begin(t), end(t));
r.sort();
for(auto& x : r)
{
std::cout << x << std::endl;
}
输出:
2
3
4
5
如何调整我的迭代器以适应 const
的情况?如果将迭代器模板化为基础类型(例如 int
)而不是 InputIt
,会更容易。有没有更好的方法来定义这个 class?
我想可以通过使用 range-v3 库来解决这个问题,但是我试图不再添加任何依赖项并依赖 C++11/14 函数。
您只是为 T
使用了错误的类型。你有:
using T = typename InputIt::value_type;
但是 value_type
对于 iterator
和 const_iterator
是一样的。他们有不同的 reference 类型。你应该更喜欢:
using R = typename std::iterator_traits<InputIt>::reference;
R operator*() { ... }
这具有使用指针的额外好处。
或者,可以通过尝试取消引用迭代器本身来避免 iterator_traits
:
using R = decltype(*std::declval<InputIt>());
旁注,sorted_range
不应该在构造上自行排序吗?否则,容易误用。
我正在尝试编写一个 class,它应该作为一些基本元素序列的排序视图。到目前为止,我想出了一个非 const
版本。现在我在调整它以提供 const_iterator
功能时遇到了问题。
我目前的代码如下所示:
// forward declare iterator
template <class InputIt>
class sorted_range_iter;
template <class InputIt>
class sorted_range {
friend class sorted_range_iter<InputIt>;
private:
using T = typename InputIt::value_type;
InputIt _first;
InputIt _last;
std::vector<size_t> _indices;
public:
using iterator = sorted_range_iter<InputIt>;
sorted_range() = default;
sorted_range(InputIt first, InputIt last)
: _first(first), _last(last), _indices(std::distance(_first, _last)) {
std::iota(_indices.begin(), _indices.end(), 0);
};
template <class Compare = std::less<T>>
void sort(Compare comp = Compare()) {
std::sort(_indices.begin(), _indices.end(),
[this, &comp](size_t i1, size_t i2) {
return comp(*(_first + i1), *(_first + i2));
});
}
size_t size() const { return _indices.size(); }
T& operator[](size_t pos) { return *(_first + _indices[pos]); }
const T& operator[](size_t pos) const { return (*this)[pos]; }
iterator begin() { return iterator(0, this); }
iterator end() { return iterator(size(), this); }
};
相应的迭代器如下所示:
template <class InputIt>
class sorted_range_iter
: public std::iterator<std::forward_iterator_tag, InputIt> {
friend class sorted_range<InputIt>;
private:
using T = typename InputIt::value_type;
size_t _index;
sorted_range<InputIt>* _range;
sorted_range_iter(size_t index, sorted_range<InputIt>* range)
: _index(index), _range(range) {}
public:
T& operator*() { return *(_range->_first + _range->_indices[_index]); }
// pre-increment
const sorted_range_iter<InputIt>& operator++() {
_index++;
return *this;
}
// post-increment
sorted_range_iter<InputIt> operator++(int) {
sorted_range_iter<InputIt> result = *this;
++(*this);
return result;
}
bool operator!=(const sorted_range_iter<InputIt>& other) const {
return _index != other._index;
}
};
用法示例如下所示:
std::vector<int> t{5, 2, 3, 4};
auto rit = ref.begin();
sorted_range<std::vector<int>::iterator> r(begin(t), end(t));
r.sort();
for(auto& x : r)
{
std::cout << x << std::endl;
}
输出:
2
3
4
5
如何调整我的迭代器以适应 const
的情况?如果将迭代器模板化为基础类型(例如 int
)而不是 InputIt
,会更容易。有没有更好的方法来定义这个 class?
我想可以通过使用 range-v3 库来解决这个问题,但是我试图不再添加任何依赖项并依赖 C++11/14 函数。
您只是为 T
使用了错误的类型。你有:
using T = typename InputIt::value_type;
但是 value_type
对于 iterator
和 const_iterator
是一样的。他们有不同的 reference 类型。你应该更喜欢:
using R = typename std::iterator_traits<InputIt>::reference;
R operator*() { ... }
这具有使用指针的额外好处。
或者,可以通过尝试取消引用迭代器本身来避免 iterator_traits
:
using R = decltype(*std::declval<InputIt>());
旁注,sorted_range
不应该在构造上自行排序吗?否则,容易误用。