basic_string
Запрещена ли COW в C ++ 11 и новее?
относительно
« Правильно ли я, что C ++ 11 не допускает реализации на основе COW std::string
?
Да.
относительно
« Если да, то указано ли это ограничение явно где-нибудь в новом стандарте (где)?
Практически напрямую, по требованиям постоянной сложности для ряда операций, которые потребовали бы O ( n ) физического копирования строковых данных в реализации COW.
Например, для функций-членов
auto operator[](size_type pos) const -> const_reference;
auto operator[](size_type pos) -> reference;
… Который в реализации COW будет «запускать копирование строковых данных для отмены совместного использования строкового значения», стандарт C ++ 11 требует
С ++ 11 §21.4.5 / 4 :
” Сложность: постоянное время.
… Что исключает такое копирование данных и, следовательно, COW.
C ++ 03 поддерживаются реализации коровы не имея эти требования постоянной сложности, и, при определенных ограничительных условиях, что позволяет звонить на operator[]()
, at()
, begin()
, rbegin()
, end()
, или rend()
о признании недействительных ссылок, указатели и итераторах со ссылкой на строковых элементы, т.е. , возможно , понесет Копирование данных COW. Эта поддержка была удалена в C ++ 11.
Является ли COW также запрещенным правилами аннулирования C ++ 11?
В другом ответе, который на момент написания был выбран в качестве решения и который получил широкое одобрение и, следовательно, очевидно, что ему верили, утверждается, что
” Для строки COW вызов non- const
operator[]
потребует создания копии (и признания недействительными ссылок), что запрещено приведенным выше абзацем [C ++ 11 §21.4.1 / 6]. Следовательно, больше не разрешено иметь строку COW в C ++ 11.
Это утверждение неверно и вводит в заблуждение по двум основным причинам:
- Это неверно указывает, что только средства
const
доступа, не относящиеся к элементам, должны запускать копирование данных COW.
Но также средства const
доступа к элементам должны запускать копирование данных, потому что они позволяют клиентскому коду формировать ссылки или указатели, которые (в C ++ 11) не разрешено аннулировать позже с помощью операций, которые могут запускать копирование данных COW.
- Он неправильно предполагает, что копирование данных COW может привести к недействительности ссылки.
Но в правильной реализации копирование данных COW без совместного использования строкового значения выполняется до того, как появятся какие-либо ссылки, которые могут быть признаны недействительными.
Чтобы увидеть, как basic_string
будет работать правильная реализация C ++ 11 COW , когда игнорируются требования O (1), которые делают это недействительным, подумайте о реализации, в которой строка может переключаться между политиками владения. Экземпляр строки начинается с политики Sharable. Если эта политика активна, ссылки на внешние элементы быть не могут. Экземпляр может перейти к уникальной политике, и он должен сделать это, когда потенциально создается ссылка на элемент, например, с вызовом .c_str()
(по крайней мере, если это создает указатель на внутренний буфер). В общем случае, когда несколько экземпляров совместно владеют значением, это влечет за собой копирование строковых данных. После этого перехода к политике уникальности экземпляр может вернуться к общему доступу только с помощью операции, которая делает недействительными все ссылки, например назначение.
Таким образом, хотя вывод из этого ответа о том, что строки COW исключены, верен, предлагаемая аргументация неверна и сильно вводит в заблуждение.
Я подозреваю, что причиной этого недоразумения является ненормативное примечание в приложении C C ++ 11:
C ++ 11 §C.2.11 [diff.cpp03.strings], о §21.3:
Изменение : basic_string
требования больше не допускают строки со счетчиком ссылок.
Обоснование: Недействительность немного отличается от строк со счетчиком ссылок. Это изменение упорядочивает поведение (так в оригинале) для данного международного стандарта.
Влияние на исходную функцию: действительный код C ++ 2003 может выполняться иначе в этом международном стандарте.
Здесь обоснование объясняет, почему было решено удалить специальную поддержку COW в C ++ 03. Это объяснение, причина , не в том, что стандарт эффективно запрещает реализацию COW. Стандарт запрещает COW через требования O (1).
Короче говоря, правила аннулирования C ++ 11 не исключают реализации COW std::basic_string
. Но они исключают достаточно эффективную неограниченную реализацию COW в стиле C ++ 03, подобную той, которая есть по крайней мере в одной из реализаций стандартной библиотеки g ++. Специальная поддержка C ++ 03 COW обеспечила практическую эффективность, в частности, использование средств const
доступа к элементам, за счет тонких, сложных правил аннулирования:
C ++ 03 §21.3 / 5, который включает поддержку COW «первый звонок»:
» Список литературы, указатели и итераторы , относящиеся к элементам basic_string
последовательности могут быть признаны недействительными следующие использования этого basic_string
объекта:
- В качестве аргумента функций , не являющихся членами swap()
(21.3.7.8), operator>>()
(21.3.7.9) и getline()
(21.3. 7,9).
- В качестве аргумента basic_string::swap()
.
- Вызов data()
и c_str()
функции-члены.
- Вызов Непро- const
функций - членов, за исключением operator[]()
, at()
, begin()
, rbegin()
, end()
, и rend()
.
- После любой из вышеуказанных целей , за исключением форм insert()
и erase()
которые возвращают итераторы, первый вызов , не являющиеся const
функции - членов operator[]()
, at()
, begin()
, rbegin()
,end()
, или rend()
.
Эти правила настолько сложны и тонки, что я сомневаюсь, что многие программисты, если таковые имеются, могут дать точное резюме. Я не мог.
Что делать, если требования O (1) игнорируются?
Если требования C ++ 11 к постоянному времени, например, operator[]
не учитываются, то COW for basic_string
может быть технически осуществимым, но трудным для реализации.
Операции, которые могут получить доступ к содержимому строки без копирования данных COW, включают:
- Конкатенация через
+
.
- Вывод через
<<
.
- Использование в
basic_string
качестве аргумента стандартных библиотечных функций.
Последнее, потому что стандартной библиотеке разрешено полагаться на конкретные знания и конструкции реализации.
Кроме того, реализация может предлагать различные нестандартные функции для доступа к содержимому строки без запуска копирования данных COW.
Основным усложняющим фактором является то, что в C ++ 11 basic_string
доступ к элементу должен запускать копирование данных (не разделять строковые данные), но не должен бросать , например, C ++ 11 §21.4.5 / 3 «Выбрасывает : ничего». И поэтому он не может использовать обычное динамическое размещение для создания нового буфера для копирования данных COW. Один из способов обойти это - использовать специальную кучу, в которой память может быть зарезервирована без фактического выделения, а затем зарезервировать необходимое количество для каждой логической ссылки на строковое значение. Резервирование и снятие резервирования в такой куче может быть постоянным временем, O (1), а выделение суммы, которая уже зарезервирована, может бытьnoexcept
, Чтобы соответствовать требованиям стандарта, при таком подходе кажется, что потребуется одна такая специальная куча на основе резервирования для каждого отдельного распределителя.
Примечания:
¹ Средство const
доступа к элементам запускает копирование данных COW, потому что оно позволяет клиентскому коду получить ссылку или указатель на данные, которые не разрешается аннулировать путем последующего копирования данных, инициированного, например, средством const
доступа, не являющимся элементом.