std :: list сторожевой узел со стандартной точки зрения - PullRequest
5 голосов
/ 16 мая 2019

Пример кода:


    list<int> mylist{10, 20, 30, 40};
    auto p = mylist.end();
    while (true)
    {
        p++;
        if (p == mylist.end()) // skip sentinel
            continue;
        cout << *p << endl;
    }

Интересно, насколько этот код допустим со стандартной (C ++ 17, n4810) точки зрения? Я ищу требования к двунаправленным итераторам, связанные с примером выше, но не повезло.

Мой вопрос:

Возможность проходить через end (), это детали реализации или это стандартные требования?

Ответы [ 2 ]

3 голосов
/ 16 мая 2019

Цитирование из последнего проекта доступны в Интернете. [iterator.requirements.general]/7

Так же, как обычный указатель на массив гарантирует, что существует значение указателя, указывающее на последний элемент массива, так и для любого типа итератора есть значение итератора, которое указывает за последним элементом соответствующей последовательности. Эти значения называются прошлыми значениями. Значения итератора i, для которых определено выражение *i, называются разыменованными. Библиотека никогда не предполагает, что значения конца-в-конце являются разыменованными.

Я считаю, что это относится не только к end(), но и к тому, что последует за этим. Обратите внимание, что в стандарте четко не указано, что end() никогда не следует разыменовывать.

И Cpp17Iterator requirements table гласит, что для выражения *r, r должно быть разыменовано:

enter image description here

Итератор конца-в-конце считается итератором без приращения, и его увеличение (как вы делаете в начале цикла while) приводит к неопределенному поведению.

При использовании std::advance может произойти нечто подобное тому, что вы пытаетесь сделать.

В книге «Стандартная библиотека C ++: учебное пособие и справочник» Николая Йозуттиса есть цитата:

Обратите внимание, что advance() не проверяет, пересекает ли он end() последовательности (он не может проверить, потому что итераторы в общем случае не знают контейнеры, с которыми они работают). Таким образом, вызов этой функции может привести к неопределенному поведению, поскольку вызывающий оператор ++ для конца последовательности не определен.

2 голосов
/ 16 мая 2019

Ваш код недопустим.Сначала вы инициализировали p как итератор прошлого конца .

auto p = mylist.end();

Теперь вы p++.Для Таблица 76 , операционная семантика r++ составляет:

{ X tmp = r;
++r;
return tmp; }

И для [Таблица 74] , ++r

Ожидается: r является разыменованным.

И за [iterator.requirements.general] / 7 ,

Библиотека никогда не предполагает, что значения из конца в конец разыменовываются.

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

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