Я реализую контейнер двумерного массива (например, boost::multi_array<T,2>
, в основном для практики). Чтобы использовать нотацию с двойным индексом (a[i][j]
), я ввел прокси-класс row_view
(и const_row_view
, но меня здесь не беспокоит постоянство), который хранит указатель на начало и конец строки.
Я также хотел бы иметь возможность перебирать строки и элементы внутри строки отдельно:
matrix<double> m;
// fill m
for (row_view row : m) {
for (double& elem : row) {
// do something with elem
}
}
Теперь класс matrix<T>::iterator
(который предназначен для перебора строк) хранит закрытый row_view rv;
внутри, чтобы отслеживать строку, на которую указывает итератор. Естественно, iterator
также реализует функции разыменования:
- для
operator*()
обычно требуется вернуть ссылку. Вместо этого здесь кажется правильным вернутьrow_view
по значению (т.е. вернуть копию частногоrow_view
). Это гарантирует, что при расширении итератораrow_view
по-прежнему будет указывать на предыдущую строку. (В некотором смыслеrow_view
действует как ссылка). для
operator->()
я не уверен. Я вижу два варианта:Верните указатель на частный
row_view
итератора:row_view* operator->() const { return &rv; }
Вернуть указатель на новый
row_view
(копию приватного). Из-за времени жизни хранилища это должно быть выделено в куче. Чтобы обеспечить очистку, я бы обернул его вunique_ptr
:std::unique_ptr<row_view> operator->() const { return std::unique_ptr<row_view>(new row_view(rv)); }
Очевидно, что 2 правильнее. Если итератор расширен после вызова operator->
, row_view
, на который указывает 1, изменится. Однако единственный способ, которым я могу думать о том, где это имело бы значение, - это если бы operator->
вызывался по его полному имени, а возвращаемый указатель был связан:
matrix<double>::iterator it = m.begin();
row_view* row_ptr = it.operator->();
// row_ptr points to view to first row
++it;
// in version 1: row_ptr points to second row (unintended)
// in version 2: row_ptr still points to first row (intended)
Однако это не то, как вы обычно используете operator->
. В таком случае вы, вероятно, вызовете operator*
и сохраните ссылку на первую строку. Обычно можно было бы немедленно использовать указатель для вызова функции-члена row_view
или доступа к члену, например. it->sum()
.
Теперь мой вопрос заключается в следующем: учитывая, что синтаксис ->
предполагает немедленное использование, считается ли допустимость указателя, возвращаемого operator->
, ограниченной этой ситуацией, или будет ли безопасная учетная запись реализации для вышеуказанного «злоупотребления "?
Очевидно, что решение 2 намного дороже, так как требует выделения кучи. Это, конечно, очень нежелательно, так как разыменование является довольно распространенной задачей, и в ней нет реальной необходимости: вместо этого использование operator*
позволяет избежать этих проблем, поскольку оно возвращает выделенную в стеке копию row_view
.
operator *
и указатель дляoperator ->
: stackoverflow.com/questions/37191290/ - person NathanOliver   schedule 26.06.2017operator*
, я не нашел никаких ограничений. Компилятор точно не жалуется. - person Jonas Greitemann   schedule 26.06.2017row_view
действует как умная ссылка. Я согласен с тем, что следует злоупотреблять перегрузкой оператора вопреки ожиданиям пользователей, но в этом случае, похоже, это соответствует ожиданиям пользователя. - person Jonas Greitemann   schedule 26.06.2017