Какво е единични и не-единични стойности в контекста на 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.

Едно от полезните неща, които можете да правите с единичен итератор, е да му присвоите неединична стойност. Когато го направите, можете да правите каквото искате с него.

Неединичните стойности са почти по дефиниция стойности на итератор, които са свързани с контейнер. Това дава отговор защо дереферируемите стойности винаги са неединични: итератори, които не сочат към никакъв контейнер, не могат да бъдат дереферирани (какъв елемент би върнал този?).

Както Matthieu M. правилно отбеляза, неединичните стойности може все още да не могат да бъдат разменени. Пример е итераторът от края (получава се чрез извикване на 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;, конструкторът по подразбиране на повечето итератори (тези, свързани с контейнер) създават единствена стойност. Тъй като не е свързан с контейнер, всяка форма на навигация (увеличаване, намаляване, ...) е безсмислена.

Как и защо преименуваните стойности винаги са неединични?

Единичните стойности по дефиниция представляват липсата на реална стойност. Те се появяват на много езици: None на Python, null на C#, NULL на C, std::nullptr на C++. Уловката е, че в C или C++ те също могат да бъдат обикновен боклук... (каквото и да е имало в паметта преди)

Конструиран по подразбиране итератор единична стойност ли е?

Не е задължително, предполагам. Това не се изисква от стандарта и може да си представите използването на обект за наблюдение.

person Matthieu M.    schedule 26.03.2011
comment
Мислех по същия начин като теб, с единични итератори, отнасящи се до неща като нулев указател или std::istream_iterator<T>(), но след това препрочетох няколко пъти цитата и стигнах до заключението, че това може да не бъде такъв. Има гаранция, че не можете само да присвоявате, но и да сравнявате нулеви указатели (или std::istream_iterator<T>() Така че вече не съм убеден, че тази интерпретация е валидна. - person David Rodríguez - dribeas; 26.03.2011
comment
@David: Мисля, че стандартът тук е неясен. Използването на повечето от последвано от единственото изключение е съмнително, защо не всички*/*с изключение? Изглежда, че са готови да отчитат и унитиализираните стойности (както в примера), а не само нулевите стойности. - 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