Почему вызывается Allocator:: reference?
Итак, я смотрел спецификацию std::vector
и заметил, что reference
typedef изменен с Allocator::reference
на С++ 03 до value_type&
на С++ 11. Я был удивлен, поэтому я начал смотреть глубже.
В С++ 03 §20.1.5 [lib.allocator.requirements] есть таблица 32, в которой X::reference
определяется как T&
, а X::const_reference
определяется как T const&
.
Однако в С++ 11 §17.6.3.5 [allocator.requirements] есть таблица 28, в которой отсутствуют reference
и const_reference
.
Далее мы добавили в С++ 11 §20.6.8 std::allocator_traits
, который не включает reference
. Но §20.6.9 std::allocator
делает.
Наконец, существует §23.2.1 [container.requirements.general], которые определяют X::reference
как "lvalue of T
" и X::const_reference
как "const lvalue of T
".
Итак, я googled и нашел этот документ (1, 2) который предлагает удалить reference
из требований распределителя, но в нем не упоминается никаких оснований. Но есть также проблема LWG, которая выступает против изменения.
Кроме того, я нашел интервью с Александром Степановым, в котором он рассказывает, как reference
инкапсулирует макет памяти для конкретной машины и Herb Sutter post, в котором он говорит о приеме указателей на элементы контейнера, требования к контейнерам и как std::vector<bool>
не является контейнером.
Итак, что вы думаете обо всем этом? Был ли полезен reference
, он служил ему цели? Как "причудливые" ссылки соответствуют стандарту? Является ли это смелым шагом, чтобы полностью устранить их, сделать более строгие требования к контейнерам и отказаться от std::vector<bool>
?
Ответы
Ответ 1
В интервью с Александром Степановым он упоминает, что во время предложения добавить STL в стандартную библиотеку ему было предложено создать абстракцию из памяти модель. Таким образом, появились распределители. В LWG проблема есть пример реализации, где reference
пользовательского распределителя определяется как T __far&
.
Но по причинам, неизвестным, потому что у меня не так много времени на поиск, стандарт С++ 03 имеет следующий текст в §20.1.5 p4:
Реализации контейнеров, описанных в этом Международном стандарте, допускают, что их параметр шаблона Allocator соответствует следующим двум дополнительным требованиям, кроме тех, что указаны в таблице 32.
- Все экземпляры данного типа распределителя должны быть взаимозаменяемыми и всегда сравнивать равными друг друга.
- указатель членов typedef, const_pointer, size_type и difference_type требуется, чтобы быть T *, T const *, size_t и ptrdiff_t соответственно.
Это эффективно нарушает способность диспетчера модели пользовательской памяти взаимодействовать со стандартными контейнерами.
Во время поиска всех документов до С++ 11, в которых упоминается слово "распределитель", я нашел большой консенсус по удалению этих слов из Стандарта. Наконец, этот документ предлагает удалить их со следующим комментарием:
Слова ласки исчезли. Поднимите свой стакан и сделайте тост.
Победа? Можем ли мы окончательно разобраться с нашими моделями памяти? Не так много. Помимо прочего, в том же документе предлагается удалить reference
из требований распределителя. И похоже, что он был принят в Стандарт.
Проблема LWG Я уже упоминал об этом, но он был закрыт следующим утверждением:
Отсутствие консенсуса относительно изменения
Итак, похоже, что первоначальная цель распределителей сегодня не так важна. Вот что Wikipedia должно сказать:
Текущая цель распределителей - дать программисту контроль над распределением памяти внутри контейнеров, а не адаптировать модель адреса базового оборудования. Фактически, пересмотренный стандарт исключил способность распределителей представлять расширения для модели адреса С++, формально (и преднамеренно) устраняя их первоначальную цель.
Наконец, Container::reference
не имеет ничего общего с распределителями. Он был создан для использования проксированных коллекций которые фактически не являются контейнерами. Так что это здесь, чтобы остаться. Кстати, похоже, что это еще один пример того, как последние слова в Стандарте противоречат оригинальным намерениям.
Ответ 2
Потому что этот вложенный typedef лишний. Эффективный STL Скотта Мейера, стр. 49:
Стандарт явно позволяет библиотечным реализаторам предположить, что каждый указатель распределителя typedef является синонимом T * и каждый ссылочный ссылочный номер-распределитель такой же, как T &
Ответ 3
http://en.wikipedia.org/wiki/Allocator_(C%2B%2B)
"Первоначально они были предназначены для того, чтобы сделать библиотеку более гибкой и независимой от базовой модели памяти, что позволяет программистам использовать пользовательский указатель и ссылочные типы с библиотекой. Однако в процессе принятия STL в стандарт С++, комитет по стандартизации С++ осознал, что полная абстракция модели памяти будет приводить к неприемлемым наказаниям за исполнение. Чтобы исправить это, требования распределителей были сделаны более ограничительными. В результате уровень настройки, предоставляемый распределителями, более ограничен, чем первоначально предусмотренный Степановым".
Первоначально они были предназначены для абстрагирования самой памяти, позволяющей выделять память, скажем, другую машину через Интернет и подключение к Интернету, и копировать данные туда и обратно с помощью указателей/ссылок, чтобы отслеживать, что происходит в реальном времени. Аналогично, можно сделать Java-подобный GC в чистом С++. Эта абстракция казалась удивительной идеей!
Однако это привело к штрафам за исполнение, которые в то время считались неприемлемыми. Кроме того, если вы думаете об этом, почти невозможно работать в коде. Каждый void func(const string&)
должен быть превращен в template<class allocator> void func(allocator::reference)
, который является невыводимым контекстом, поэтому вам нужно будет явно написать распределитель в вызове функции (func<std::allocator<std::string>::const_reference>(username)
), который никто не сделает, что сделало бы GC не работает должным образом. В настоящее время распределители просто абстрагируют выделение/освобождение памяти.