Есть две основные заметки, которые нужно сказать, чтобы сделать эту работу. Во-первых:
Если я заставлю конструктор It принимать контейнер const, он не будет выглядеть правильно, потому что итератор может изменить содержимое контейнера.
Не совсем, потому что C
в вашем template<typename C> class It
это не фактический контейнер, а span<V>
. Другими словами, взгляните на:
It<self_type> begin() const { return It<self_type>(*this, 0); }
Здесь self_type
означает const span<V>
, поэтому вы возвращаете It<const span<V>>
. Таким образом, ваш итератор может делать все, что может быть сделано с const span
- но контейнер все еще не - const
. Тогда имя переменной container_
не является удачным.
Для типа T
, который не const
, и const span<T>
, и span<T>
позволяют изменять содержимое контейнера. И const span<const T>
, и span<const T>
запрещают изменять содержимое контейнера.
Кроме того, поскольку вы хотите, чтобы const span
было разрешено изменять содержимое, то то, что вы должны написать внутри самого span
(обратите внимание на const
):
int& operator[](const int ix) const {return v_[ix];}
// Removing the other `const` version:
// const int& operator[](const int ix) const {return v_[ix];}
После прояснения этих двух битов вы можете создать рабочий пример. Вот пример, основанный на вашем коде и упрощенный для решения данной проблемы:
#include <vector>
#include <iostream>
template<typename S>
class It {
typedef It<S> self_type;
const S& span_;
int ix_;
public:
It(const S& span, const int ix)
: span_(span), ix_(ix) {}
self_type& operator++() {
++ix_;
return *this;
}
int& operator*() const {
return span_[ix_];
}
bool operator!=(const self_type& rhs) const {
return &span_ != &rhs.span_ or ix_ != rhs.ix_;
}
};
template <typename V>
class span {
typedef span<V> self_type;
public:
explicit span(V& v) : v_(v) {}
It<self_type> begin() const { return It<self_type>(*this, 0); }
It<self_type> end() const { return It<self_type>(*this, v_.size()); }
int& operator[](const int ix) const {return v_[ix];}
private:
V& v_;
};
int main() {
typedef std::vector<int> V;
V v(10);
const span<V> s(v);
for (auto&& x : s) {
x = 4;
std::cout << x << "\n";
}
}
Обратите также внимание на исправленную реализацию operator!=
и на тот факт, что нет необходимости в const
и end()
не-1043 * версиях. Вы также можете бросить туда cbegin()
и cend()
. Затем вам нужно работать над добавлением обратно константных итераторов.
Кстати, в случае, если это спасет кого-то от путаницы: в ближайшее время может быть добавлено std::span
( предлагается для C ++ 20 ); и это будет просто пара (pointer-to-first-element, index)
, а не ваша (pointer-to-container, index)
версия.
Другими словами, в качестве параметра шаблона он будет принимать тип элементов, а не контейнер:
span<std::vector<int>> s(v);
// vs
std::span<int> s(v);
Это позволяет потребителям std::span
избегать знания о том, какой контейнер находится за кадром (или даже нет контейнера: непрерывная область памяти или массив).
Наконец, вы можете взглянуть на реализацию GSL std::span
, чтобы получить некоторое представление о том, как полностью реализовать его (включая второй параметр шаблона о экстенте).