Что такое особые и неособые значения в контексте итераторов STL?

Раздел §24.1 / 5 стандарта C ++ (2003) гласит:

Так же, как обычный указатель на массив гарантирует, что существует значение указателя, указывающее за последним элементом массива, так и для любого типа итератора существует значение итератора, которое указывает за последний элемент соответствующего контейнера. Эти значения называются значениями после окончания. Значения итератора i, для которого определено выражение * i, называются разыменовываемыми. Библиотека никогда не предполагает, что последние значения могут быть разыменованы. Итераторы также могут иметь особые значения, которые не связаны с каким-либо контейнером. [Пример: после объявления неинициализированного указателя x (как в случае с int * x;) всегда следует предполагать, что x имеет единственное число. значение указателя.] Результаты большинства выражений не определены для сингулярных значений; единственное исключение - это присвоение неособого значения итератору, который содержит единственное значение. В этом случае единственное значение перезаписывается так же, как и любое другое значение. Разыменовываемые значения всегда неособые.

Я не могу понять текст, выделенный полужирным?

  • Что такое единственное и несингулярное значение? Как они определены? И где?
  • Как и почему разыменовываемые значения всегда неособые?

person Nawaz    schedule 26.03.2011    source источник


Ответы (4)


Итераторы также могут иметь особые значения, которые не связаны ни с каким контейнером.

Полагаю, это его определение.

Как и почему разыменовываемые значения всегда неособые?

Потому что, если бы они этого не сделали, разыменование их было бы неопределенным поведением.

person Johannes Schaub - litb    schedule 26.03.2011

Если я правильно понимаю, сингулярное значение для итератора по сути эквивалентно неназначенному указателю. Это итератор, который нигде не инициализирован, и поэтому у него нет четко определенного элемента, который он повторяет. Объявление нового итератора, который не настроен для указания на элемент диапазона, например, создает этот итератор как единственный итератор.

Как указано в части спецификации, единичные итераторы небезопасны, и ни одна из стандартных операций итератора, таких как приращение, присваивание и т. Д., Не может использоваться с ними. Все, что вы можете сделать, это присвоить им новое значение, надеясь, что они будут указывать на действительные данные.

Я думаю, что причина такого определения в том, что такие утверждения, как

set<int>::iterator itr;

Может быть разрешено спецификацией при стандартизованном значении. Термин «сингулярность» здесь, вероятно, относится к математическому определению сингулярности, которую в менее формальных условиях также называют «разрывом».

person templatetypedef    schedule 26.03.2011
comment
Можно ли считать созданный по умолчанию istream_iterator единичным? - person Björn Pollex; 26.03.2011
comment
Я так не думаю. Это действительный итератор, прошедший через конец. Если бы он был единичным, вы не могли бы использовать его для определения enf потока, поскольку вы не могли бы сравнить его с любыми другими итераторами. - person templatetypedef; 26.03.2011

Взгляните на Что такое значение итератора по умолчанию?.

Как указывает цитата, особые значения - это значения итератора, не связанные ни с одним контейнером. Единственное значение почти бесполезно: вы не можете продвигать его, разыменовать его и т. Д. Один из способов (единственный способ?) Получить единичный итератор - не инициализировать его, как показано в ответе templatetypedef.

Одна из полезных вещей, которые вы можете сделать с помощью единственного итератора, - это присвоить ему неособое значение. Когда вы это сделаете, вы сможете делать с ним все, что захотите.

Неособые значения почти по определению являются значениями итератора, которые связаны с контейнером. Это отвечает на вопрос, почему разыменовываемые значения всегда не являются единственными: итераторы, которые не указывают на какой-либо контейнер, не могут быть разыменованы (какой элемент это вернет?).

Как правильно заметил Матье М., неособые значения могут по-прежнему не разыменовываться. Примером является итератор, прошедший через конец (который можно получить, вызвав container.end ()): он связан с контейнером, но по-прежнему не может быть указан.

Я не могу сказать, где эти термины определены. Тем не менее, Google говорит об "define: singular" (среди других определений) следующее:

remarkable: unusual or striking

Я полагаю, это может объяснить терминологию.

person telewin    schedule 26.03.2011
comment
Осторожно! Неособые значения включают прошедший конечный итератор, разыменовать его нельзя. - person Matthieu M.; 26.03.2011
comment
@Matthieu: ты прав на 100%. Я обновил свой ответ, чтобы отразить, что неособые значения все еще могут быть неразменяемыми. Спасибо! - person telewin; 26.03.2011

Что такое единственное и несингулярное значение? Как они определены? И где?

Давайте воспользуемся простейшим воплощением Iterator: указателем.

Для указателя:

  • указанное единственное значение - это значение NULL неинициализированное значение.
  • неособое значение - это явно инициализированное значение, оно не может быть разыменовано по-прежнему (указатель на конец конца не должен разыменовываться)

Я бы сказал, что указатель NULL - это единственное значение, но не единственное, поскольку оно представляет отсутствие значения.

Какая эквивалентность для обычных итераторов?

std::vector<int>::iterator it;, конструктор по умолчанию для большинства итераторов (связанных с контейнером), создает единственное значение. Поскольку он не привязан к контейнеру, любая форма навигации (увеличение, уменьшение, ...) не имеет смысла.

Как и почему разыменовываемые значения всегда неособые?

Особые значения по определению представляют собой отсутствие реальной стоимости. Они появляются на многих языках: Python None, C # null, C NULL, C ++ std::nullptr. Загвоздка в том, что в C или C ++ они также могут быть простым мусором ... (все, что было в памяти раньше)

Является ли построенный по умолчанию итератор сингулярным значением?

Думаю, не обязательно. Стандарт не требует этого, и можно представить себе использование дозорного объекта.

person Matthieu M.    schedule 26.03.2011
comment
Я думал о тех же строках, что и вы, с итераторами singular, относящимися к таким вещам, как нулевой указатель или std::istream_iterator<T>(), но затем я пару раз перечитал цитату и пришел к выводу, что это может не быть так. Есть гарантия, что вы не только присваиваете, но и сравниваете нулевые указатели (или std::istream_iterator<T>(). Так что я больше не уверен, что эта интерпретация верна. - person David Rodríguez - dribeas; 26.03.2011
comment
@ Дэвид: Я думаю, что стандарт здесь неясен. Использование большей части, за которым следует единственное исключение сомнительно, почему бы не all * / * except? Похоже, они были готовы учитывать и унифицированные значения (как в примере), а не только нулевые значения. - person Matthieu M.; 26.03.2011
comment
указанное единственное значение на самом деле не NULL. Цитируемый пример текста ясен: после объявления неинициализированного указателя x (как в случае с int* x;) всегда следует предполагать, что x имеет сингулярное значение указателя. Теперь неинициализированный указатель - это не NULL. Вы даже не можете сравнить это с NULL. - person MSalters; 28.03.2011
comment
@MSalters: предполагалось исправить, но я пропустил /, чтобы закрыть тег strike :) - person Matthieu M.; 28.03.2011