насколько переносим конечный итератор? - PullRequest
51 голосов
/ 16 марта 2011

Только что обнаружил уменьшение итератора end() в исходных кодах моей компании, и это выглядит странно для меня. Насколько я помню, это работало на некоторых платформах, но не на других. Может быть, я ошибаюсь, однако я не нашел ничего стандартного в этом. Стандарт только говорит, что end() возвращает итератор, который является последним значением, но гарантированно ли он может быть уменьшаемым? Как такой код соответствует стандарту?

std::list<int>::iterator it = --l.end();

Заранее спасибо.

Ответы [ 3 ]

50 голосов
/ 16 марта 2011

Я думаю, что это соответствующий пункт:

ISO / IEC 14882: 2003 C ++ Стандарт 23.1.1 / 12 - Последовательности

Таблицы 68 списковОперации последовательности, которые предоставляются для некоторых типов последовательных контейнеров, но не для других.Реализация должна предоставлять эти операции для всех типов контейнеров, показанных в столбце «контейнер», и должна реализовывать их так, чтобы принимать амортизированное постоянное время.если итератор, возвращаемый из end(), может быть декремментируемым, декрементированный итератор также должен быть разыменованным.(Если, конечно, контейнер не пустой, это вызывает неопределенное поведение.)

Фактически, реализации vector, list и deque, поставляемые с компилятором Visual C ++, делают это точно так же, как таблица,Конечно, это не означает, что каждый компилятор делает это так:

// From VC++'s <list> implementation

reference back()
    {    // return last element of mutable sequence
    return (*(--end()));
    }

const_reference back() const
    {    // return last element of nonmutable sequence
    return (*(--end()));
    }

Примечание о коде в таблице:

ISO / IEC14882: 2003 C ++ Standard 17.3.1.2/6 - Требования

В некоторых случаях семантические требования представлены в виде кода C ++. Такой код предназначен для спецификации эквивалентности конструкции другой конструкции , а не обязательно как способ, которым конструкция должна быть реализована.

Таким образом, правда, что реализация можетне реализуя эти выражения в терминах begin() и end(), стандарт C ++ указывает, что оба выражения эквивалентны.Другими словами, a.back() и *--a.end() являются эквивалентными конструкциями согласно приведенному выше пункту.Мне кажется, что это означает, что вы должны иметь возможность заменить каждый экземпляр a.back() на *--a.end() и наоборот, а код все еще работает.


Согласно Бо Перссону,пересмотренный стандарт C ++, который у меня на руках , имеет дефект относительно таблицы 68.

Предлагаемое разрешение:

Изменитьспецификация в таблице 68 «Дополнительные операции последовательности» в 23.1.1 / 12 для «a.back ()» от

*--a.end()

до

{ iterator tmp = a.end(); --tmp; return *tmp; }

и спецификация для «a.pop_back () "с

a.erase(--a.end())

до

{ iterator tmp = a.end(); --tmp; a.erase(tmp); }

Похоже, что вы все еще можете уменьшить итератор, возвращенный с end(), и разыменовать уменьшенный итератор до тех пор, покакак это не временно.

2 голосов
/ 28 декабря 2018

из документации на std::prev

Хотя выражение --c.end () часто компилируется, это не гарантируется: c.end () является выражением rvalue, и не существует требования к итератору, указывающего, что уменьшение значения r гарантирует значение Работа. В частности, когда итераторы реализованы как указатели, --c.end () не компилируется, а std :: prev (c.end ()) -.

, что означает, что реализация операции декремента префикса может быть не внутри класса форма iterator iterator::operator--(int), но перегружена вне формы класса iterator operator--(iterator&, int).

, поэтому вы должны либо предпочесть std::prev, либо сделать следующее: { auto end = a.end(); --end; };

0 голосов
/ 16 марта 2011

Этот код не в порядке, если список пуст, у вас проблемы.

Так проверьте, если список не пуст, код очень хорош.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...