Говоря о стандартных итераторах, это зависит от нескольких вещей:
Для соответствия Итератор s, почти не имеет значения, что тип reference
, потому что стандарт не требует никакой семантики использования для типа reference
. Но это также означает, что никто, кроме вас, не знает, как использовать ваш итератор.
Для соответствия Итератор ввода s, тип reference
должен соответствовать указанной семантике. Обратите внимание, что для LegacyInputIterator
выражение *it
должно быть ссылкой, которую можно использовать как ссылку со всей нормальной семантикой, в противном случае код, использующий ваш итератор, не будет работать должным образом. Это означает, что чтение из reference
сродни чтению со встроенной ссылки. В частности, следующее должно делать «нормальные» вещи:
auto value = *itr; // this should read a value
В этой ситуации тип представления, такой как span
, не будет работать, потому что span
больше похож на указатель, чем на ссылку: в приведенный выше фрагмент value
будет span
, не смотря на то, на что ссылается span
.
Для соответствия Итератор вывода s, тип reference
не имеет требований. Фактически, стандартные LegacyOutputIterator
s, такие как std::back_insert_iterator
, имеют void
как reference
тип.
Для соответствия Forward Iterator s и выше, стандарт фактически требует reference
быть встроенной ссылкой. Это для поддержки использования, как показано ниже:
auto& ref = *itr;
auto ptr = &ref; // this must create a pointer pointing to the original object
auto ref2 = *ptr; // this must create a second, equivalent reference
auto other = std::move( ref ); // this must do a "move", which may be the same as a copy
ref = other; // this must assign "other"'s value back into the referred-to object
Если вышеупомянутое не сработало правильно, многие из стандартных алгоритмов не могли бы быть написаны в общем.
Говоря с span
в частности, он действует скорее как указатель, чем как ссылка логически. Это может быть переназначено, чтобы указать на что-то еще. Взяв его адрес, вы создаете указатель на span
, а не указатель на охватываемый контейнер. Вызов std::move
для диапазона копирует диапазон и не перемещает содержимое диапазона. Встроенная ссылка T&
будет ссылаться только на одну вещь после ее создания.
Создание несоответствующего reference
, который фактически работает со стандартными алгоритмами, потребует перегрузки семейства типов operator*
, operator->
и operator&
, operator=
и std::move
, а также указатели моделирования, ссылки на lvalue и ссылки на rvalue.