Запрещен ли COW basic_string
в C++11 и более поздних версиях?
Касательно
Правильно ли я понимаю, что C++11 не поддерживает реализацию std::string
на основе COW?
Да.
Касательно
Если да, указано ли это ограничение явно где-то в новом стандарте (где)?
Почти напрямую, по требованиям постоянной сложности для ряда операций, которые потребуют O(n) физического копирования строковых данных в COW выполнение.
Например, для функций-членов
auto operator[](size_type pos) const -> const_reference;
auto operator[](size_type pos) -> reference;
что в реализации COW ¹оба инициируют копирование строковых данных, чтобы отменить совместное использование строкового значения, стандарт C++11 требует
C++11 §21.4.5/4:
Сложность: постоянное время.
что исключает такое копирование данных и, следовательно, COW.
C++03 поддерживал реализации COW, отсутствие этих требований к постоянной сложности и, при определенных ограничительных условиях, разрешая вызовы operator[]()
, at()
, begin()
, rbegin()
, end()
или rend()
для аннулирования ссылок, указателей. итераторы, относящиеся к элементам строки, т.е. для возможного копирования данных COW. Эта поддержка была удалена в C++11.
Запрещен ли COW также правилами аннулирования C++11?
В другом ответе, который на момент написания был выбран в качестве решения, за который активно проголосовали и, следовательно, по-видимому, ему поверили, утверждается, что
Для строки COW вызов non-const
operator[]
потребует создания копии (и аннулирования ссылок), что запрещено абзацем [в кавычках] выше [C++11 §21.4.1/6]. . Следовательно, в С++ 11 больше не разрешено иметь строку COW.
Это утверждение неверно и вводит в заблуждение по двум основным причинам:
- Это неверно указывает, что только методы доступа к элементам, отличные от
const
, должны инициировать копирование данных COW.
Но также методы доступа к элементам const
должны запускать копирование данных, поскольку они позволяют клиентскому коду формировать ссылки или указатели, которые (в C++11) не разрешается позже аннулировать с помощью операций, которые могут инициировать копирование данных COW.
- Ошибочно предполагается, что копирование данных COW может привести к аннулированию ссылки.
Но в правильной реализации копирование данных COW, отменяющее совместное использование строкового значения, выполняется до того, как появятся какие-либо ссылки, которые могут быть признан недействительным.
Чтобы увидеть, как будет работать правильная реализация C++11 COW для basic_string
, когда игнорируются требования O(1), делающие это недействительным, подумайте о реализации, в которой строка может переключаться между политиками владения. Экземпляр строки начинается с политики Sharable. Если эта политика активна, ссылки на внешние элементы быть не могут. Экземпляр может перейти к уникальной политике, и он должен сделать это, когда потенциально создается ссылка на элемент, например, при вызове .c_str()
(по крайней мере, если это создает указатель на внутренний буфер). В общем случае, когда несколько экземпляров совместно владеют значением, это влечет за собой копирование строковых данных. После этого перехода к уникальной политике экземпляр может вернуться обратно к совместно используемому только с помощью операции, которая делает недействительными все ссылки, например назначение.
Таким образом, хотя вывод этого ответа о том, что строки COW исключены, является правильным, предлагаемое рассуждение неверно и сильно вводит в заблуждение.
Я подозреваю, что причиной этого недоразумения является ненормативное примечание в приложении C C++11:
C++11 §C.2.11 [diff.cpp03.strings], about §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 which includes “first call” COW support:
Ссылки, указатели и итераторы, относящиеся к элементам последовательности 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()
, которые возвращают итераторы, первый вызов не-36_ функций-членов operator[]()
, at()
, begin()
, rbegin()
, end()
или rend()
.
Эти правила настолько сложны и изощренны, что я сомневаюсь, что многие программисты, если вообще найдутся, смогут дать точную сводку. Я не могу.
Что делать, если требования O(1) игнорируются?
Если требования постоянного времени С++ 11, например. operator[]
игнорируются, то COW для basic_string
может быть технически осуществимым, но сложным в реализации.
Операции, которые могут получить доступ к содержимому строки без копирования данных COW, включают:
- Объединение через
+
.
- Выход через
<<
.
- Использование
basic_string
в качестве аргумента для функций стандартной библиотеки.
Последнее потому, что стандартной библиотеке разрешено полагаться на знания и конструкции, специфичные для реализации.
Кроме того, реализация может предлагать различные нестандартные функции для доступа к содержимому строки без запуска копирования данных COW.
Основным усложняющим фактором является то, что в С++ 11 basic_string
доступ к элементу должен инициировать копирование данных (отмена совместного использования строковых данных), но требуется не бросать, например. C++11 §21.4.5/3 Выдает: Ничего.. Таким образом, он не может использовать обычное динамическое выделение для создания нового буфера для копирования данных COW. Один из способов обойти это — использовать специальную кучу, в которой можно зарезервировать память без ее фактического выделения, а затем резервировать необходимый объем для каждой логической ссылки на строковое значение. Резервирование и разрезервирование в такой куче может быть постоянным временем, O(1), а выделение суммы, которая уже зарезервирована, может быть noexcept
. Чтобы соответствовать требованиям стандарта, при таком подходе кажется, что должна быть одна такая специальная куча на основе резервирования для каждого отдельного распределителя.
Примечания.
¹ Метод доступа к элементу const
инициирует копирование данных COW, поскольку он позволяет клиентскому коду получить ссылку или указатель на данные, которые нельзя аннулировать более поздним копирование данных, вызванное, например, метод доступа к элементу, отличный от const
.
person
Cheers and hth. - Alf
schedule
29.07.2016