Должен ли std :: iterator :: reference быть ссылкой? - PullRequest
3 голосов
/ 31 января 2020

Я реализовал пользовательский итератор, основанный на ответе здесь:
{ ссылка }

Однако вместо члена Type* _ptr мой код в конечном итоге получает значение хранится в каком-то единственном месте, как если бы он использовал следующую функцию:

// my iterator class gets its values from this function
float globalFloat{ 0 };
float* retrieveFloat (int idx)
{
    globalFloat = (float)idx;
    return &globalFloat;
}

Это означает, что для одновременного использования двух итераторов (т. е. для поиска с использованием upper_bound), я должен кешировать локально перед тем, как разрешить доступ:

class Iterator : public std::iterator<std::random_access_iterator_tag, float, int>
{
public:
    Iterator () = default;
    Iterator (int idx) : _index (idx) {}

    Iterator& operator++ () noexcept { ++_index; return *this; }
    Iterator& operator-- () noexcept { --_index; return *this; }

    /// ... rest of iterator declaration

    const float& operator* () const { _data = *retrieveFloat (_index); return _data; }
    const float* operator-> () const { _data = *retrieveFloat (_index); return &this->_data; }
    const float& operator[] (int offset) const { _data = *retrieveFloat (_index + offset); return _data; }

private:
    int _index{ 0 };
    mutable float _data{ 0 };
};

Меня беспокоит последний оператор []. Согласно cppreference:
https://en.cppreference.com/w/cpp/named_req/RandomAccessIterator

оператор [] должен возвращать ссылочный тип. Однако, если бы я написал следующий код:

int main (int argc, char ** argv)
{
    Iterator it;
    if (it[0] == it[1])
        return 0;
    return 1;
}

Тогда я верну 0, потому что каждый вызов [] изменяет _data.

Если я изменю способ подкласса std :: iterator и использую float в качестве моего "ссылочного" типа:

class Iterator : public std::iterator<std::random_access_iterator_tag, float, int, float*, float>
{
public:
    /// ... rest of iterator declaration (constructors / operators)

    const float operator* () const { _data = *retrieveFloat (_index); return _data; }
    const float* operator-> () const { _data = *retrieveFloat (_index); return &this->_data; }
    const float operator[] (int offset) const { _data = *retrieveFloat (_index + offset); return _data; }
};

, тогда все будет работать ... но почему-то это выглядит грязно. Делать подобные вещи законно?

Я знаю, что если бы вместо float мой тип данных был чем-то более тяжелым для копирования, то возникла бы проблема с производительностью, но ради аргумента скажем, что я работаю только с float или облегченными POD. Давайте также предположим, что мне не нужно изменять итерируемые данные.

Спасибо за любую помощь, которую вы можете оказать, и извините, если я задал этот вопрос слишком долго. Я могу редактировать, если это будет необходимо.

  • Джон

1 Ответ

3 голосов
/ 31 января 2020

Должен ли std :: iterator :: reference быть ссылкой?

Нет. std::iterator::reference не обязательно должен быть ссылочным типом. Он просто должен быть того же типа, который был возвращен operator*, и должен быть конвертируемым в value_type.

Однако, чтобы быть OuputIterator, *r = o должен быть правильно сформирован и как таковой reference должен быть либо ссылочным типом, либо типом класса с оператором присваивания с ненулевым значением ref. Таким образом, использование float хорошо для не итераторов вывода, но не подходит для итераторов вывода.

...