Подтвердить что ты не робот

Насколько переносимым является декремент концевого итератора?

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

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

Спасибо заранее.

4b9b3361

Ответ 1

Я думаю, что это соответствующее предложение:

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

В таблице 68 перечислены операции последовательности которые предоставляются для некоторых типов последовательные контейнеры, но не другие. Реализация должна обеспечивать операции для всех типов контейнеров показано в столбце "контейнер" и должны осуществлять их, чтобы амортизированное постоянное время.

    +----------------------------------------------------------------------------+
    |                                  Table 68                                  |
    +--------------+-----------------+---------------------+---------------------+
    |  expression  |   return type   |     operational     |      container      |
    |              |                 |      semantics      |                     |
    +--------------+-----------------+---------------------+---------------------+
    | a.front()    | reference;      | *a.begin()          | vector, list, deque |
    |              | const_reference |                     |                     |
    |              | for constant a  |                     |                     |
    +--------------+-----------------+---------------------+---------------------+
    | a.back()     | reference;      | *--a.end()          | vector, list, deque |
    |              | const_reference |                     |                     |
    |              | for constant a  |                     |                     |
    ..............................................................................
    .              .                 .                     .                     .
    .              .                 .                     .                     .
    ..............................................................................
    | a.pop_back() | void            | a.erase(--a.end())  | vector, list, deque |
    ..............................................................................
    .              .                 .                     .                     .
    .              .                 .                     .                     .

Итак, для перечисленных контейнеров не только должен возвращаться итератор из end(), декрементный итератор также должен быть разуплотненным. (Если контейнер не пуст, конечно, это вызывает поведение undefined.)

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

// From VC++ <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/IEC 14882: 2003 Стандарт С++ 17.3.1.2/6 - Требования

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

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


Согласно Бо Перссону, пересмотр стандарта С++, который у меня под рукой имеет дефект относительно таблицы 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

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

Поэтому проверьте это, если список не пуст, код очень тонкий.