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доступа, не являющимся элементом.