Законность реализации COW std:: string в C++11

насколько я понимаю, копирование на запись не является жизнеспособным способом реализации соответствия std::string в C++11, но когда он недавно появился в обсуждении, я обнаружил, что не могу напрямую поддержать это утверждение.

Я прав, что C++11 не допускает реализации на основе коровы std::string?

если да, то это ограничение явно указано где-то в новом стандарте (где)?

или это ограничение подразумевается, в том смысле, что это комбинированное воздействие новых требований на std::string Это исключает реализацию коровы на основе std::string. В этом случае мне было бы интересно получить деривацию стиля главы и стиха " C++11 эффективно запрещает коровуstd::string реализаций'.

6 ответов


это не разрешено, потому что в соответствии со стандартом 21.4.1 p6 недействительность итераторов/ссылок допускается только для

- в качестве аргумента для любой стандартной библиотеки функция принимает ссылку не-const basic_string в качестве аргумента.

- вызов non-const функции-члены, кроме operator [], at, front, back, begin, rbegin, конец и разрыв.

для строки коровы вызов non-const operator[] потребуется сделать копию (и недействительные ссылки), который запрещен пунктом выше. Следовательно, больше не законно иметь строку коровы в C++11.


ответы Дейв С и gbjbaanb are правильно. (И Люк Дантон тоже прав, хотя это скорее побочный эффект запрета коровьих струн, а не оригинальное правило, которое запрещает это.)

но чтобы прояснить некоторую путаницу, я собираюсь добавить еще одно объяснение. Различные комментарии ссылка на мой комментарий о GCC bugzilla который дает следующий пример:

std::string s("str");
const char* p = s.data();
{
    std::string s2(s);
    (void) s[0];
}
std::cout << *p << '\n';  // p is dangling

в точка этого примера-продемонстрировать, почему строка подсчета ссылок GCC (COW) недопустима в C++11. Стандарт C++11 требует, чтобы этот код работал правильно. Ничто в коде не позволяет p недействительным в C++11.

использование старой ссылки GCC-подсчитано std::string реализации, что код имеет неопределенное поведение, потому что p is признан недействительным, став болтающимся указателем. (Что происходит, когда s2 построено оно делит данные с s, но получение ссылки non-const через s[0] требует, чтобы данные не были разделены, поэтому s делает "копию на записи", потому что ссылка s[0] потенциально может использоваться для записи в s, потом s2 выходит за рамки, уничтожая массив, на который указывает p).

стандарт C++03 явно разрешает такое поведение в 21.3 [lib.основной.string] p5, где говорится, что после вызова data() первый вызов operator[]() может аннулировать указатели, ссылки и итераторы. Таким образом, строка коровы GCC была допустимой реализацией C++03.

стандарт C++11 больше не разрешает такое поведение, потому что нет вызова operator[]() может аннулировать указатели, ссылки или итераторы, независимо от того, следуют ли они вызову data().

Итак, пример выше должны работа в C++11, но не работа с видом строки коровы libstdc++, поэтому этот вид строка коровы не допускается в C++11.


Это, корова является приемлемым механизмом для создания более быстрых строк... но...

это делает многопоточный код медленнее (вся эта блокировка, чтобы проверить, являетесь ли вы единственным, кто пишет, убивает производительность при использовании большого количества строк). Это была главная причина, по которой корову убили много лет назад.

другие причины:[] оператор вернет вам строковые данные без какой-либо защиты для перезаписи строки, которую кто-то другой ожидает неизменной. Тот же относится к c_str() и data().

быстрый google говорит, что многопоточность в основном причине он был эффективно запрещен (не явно).

предложения :

предложение

мы предлагаем сделать все операции доступа к итератору и элементу безопасными одновременно исполняемый файл.

мы увеличиваем стабильность операций даже в последовательном коде.

этот изменение эффективно запрещает реализацию копирования при записи.

следовал по

самая большая потенциальная потеря в представлении должном к переключателю далеко от реализация copy-on-write-это повышенное потребление памяти для приложений с очень большими строками чтения. Однако мы поверьте что для тех применений веревочки лучшее техническое решение, и рекомендует предложение веревочки быть рассмотренным для включения в Библиотека ТР2.

веревки являются частью STLPORT и SGIs STL.


из 21.4.2 basic_string конструкторы и операторы присваивания [string.минусы]

basic_string(const basic_string<charT,traits,Allocator>& str);

[...]

2 эффекты: создает объект класса basic_string как указано в таблице 64. [...]

таблица 64 услужливо документирует, что после построения объекта через этот (копировать) конструктор,this->data() имеет значение as:

указывает на первый элемент выделенного копия массив, первый элемент которого указывает на стр.data ()

существуют аналогичные требования для других подобных конструкторов.


поскольку теперь гарантируется, что строки хранятся смежно, и теперь вам разрешено взять указатель на внутреннее хранилище строки (т. е. &str[0] работает так же, как для массива), невозможно сделать полезную реализацию коровы. Вам придется сделать копию для слишком многих вещей. Даже просто используя operator[] или begin() для строки без const потребуется копия.


это корова basic_string запрещено в C++11 и позже?

о

" я прав, что C++11 не допускает реализации на основе коровы std::string?

да.

о

" если да, то это ограничение явно указано где-то в новом стандарте (где)?

почти непосредственно, по требованиям постоянной сложности для ряда операций, которые потребуют O (n) физическое копирование строковых данных в реализации COW.

например, для функций-членов

auto operator[](size_type pos) const -> const_reference;
auto operator[](size_type pos) -> reference;

... который в реализации коровы будет 1both копировать строковые данные триггера в un-share строковое значение, стандарт C++11 требуется

C++11 §21.4.5 / 4:

" сложность: постоянное время.

... что исключает такое копирование данных, и, следовательно, корова.

C++03 поддерживаемые реализации коровы по не имея эти постоянные требования сложности, и, при определенных ограничительных условиях, позволяя звонки operator[](), at(), begin(), rbegin(), end() или rend() недействительным ссылки, указатели и итераторы, ссылающиеся на строковые элементы, т. е. Возможно, для копирования данных коровы. Эта поддержка была удалена в C++11.


корова также запрещена через правила недействительности c++11?

в другом ответе, который на момент написания выбран в качестве решения, и который сильно поднят и поэтому, по-видимому, считается, утверждается, что

" для строки коровы, вызываю не -const operator[] потребуется сделать копию (и недействительные ссылки), которая запрещена [процитированным] абзацем выше [C++11 §21.4.1/6]. Следовательно, больше не законно иметь строку коровы в C++11.

это утверждение неверно и вводит в заблуждение двумя основными способами:

  • это неправильно указывает на то, что только не -const элементы доступа должны вызвать копирование данных коровы.
    Но и const методы доступа к элементам должны инициировать копирование данных, поскольку они позволяют клиентскому коду формировать ссылки или указатели, которые (в C++11) не разрешается аннулировать позже с помощью операций, которые могут инициировать копирование данных COW.
  • неверно предполагается, что копирование данных COW может привести к недействительности ссылки.
    Но в правильной реализации копирование данных коровы, un-sharing строковое значение, делается в точке, прежде чем есть какие-либо ссылки, которые могут быть лишать законной силы.

чтобы увидеть, как правильная реализация коровы C++11 basic_string будет работать, когда требования O(1), которые делают это недопустимым, игнорируются, подумайте о реализации, где строка может переключаться между политиками владения. Экземпляр string начинается с политики Sharable. При активной политике ссылки на внешние элементы отсутствуют. Экземпляр может перейти к уникальной политике, и он должен сделать это, когда ссылка на элемент потенциально создается, например, с помощью вызов .c_str() (по крайней мере, если это создает указатель на внутренний буфер). В общем случае нескольких экземпляров, совместно владеющих значением, это влечет за собой копирование строковых данных. После этого перехода к уникальной политике экземпляр может вернуться к Sharable только с помощью операции, которая аннулирует все ссылки, такие как присваивание.

таким образом, хотя вывод этого ответа, что коровьи строки исключены, является правильным, предлагаемые рассуждения неверны и сильно вводящий в заблуждение.

я подозреваю, что причиной этого недоразумения является ненормативная записка в приложении c++11:

В C++11 §В. 2.11 [дифф.cpp03.strings], о §21.3:

изменить: basic_string требования больше не разрешают строки с подсчетом ссылок
обоснование: недействительность тонко отличается от ссылочных строк. Это изменение регулирует поведение (sic) для данного международного Норматив.
влияние на первоначально функции: допустимый код C ++ 2003 может выполняться по-разному в этом международном стандарте

здесь обоснование объясняет главную почему один решил удалить специальную поддержку коровы c++03. Это обоснование, то почему, не как стандарт эффективно запрещает реализацию коровы. Стандарт запрещает корову через O(1) требования.

короче говоря, правила недействительности C++11 не исключают реализацию коровы std::basic_string. Но они исключают разумно эффективную неограниченную реализацию коровы в стиле c++03, Как, по крайней мере, в одной из стандартных реализаций библиотеки g++. Специальная поддержка коровы C++03 позволила практически эффективности, в частности используя const элементы доступа, за счет тонких, сложных правил недействительности:

C++03 §21.3 / 5 которая включает в себя "первый звонок" коровьей поддержки:

" ссылки, указатели и итераторы, относящиеся к элементам basic_string последовательность может быть признана недействительной при следующем использовании этого и c_str() функции-члены.
- Вызываю не -const член функции, кроме operator[](), at(), begin(), rbegin(), end() и rend().
- После любого из вышеуказанных видов использования, кроме форм insert() и erase(), которые возвращают итераторы, первый вызов неconst функции-члены operator[](), at(), begin(), rbegin(), end() или rend().

эти правила настолько сложны и тонки, что я сомневаюсь, что многие программисты, если таковые имеются, могли бы дать точное резюме. Я мог бы не.


что делать, если требования O(1) игнорируются?

если требования постоянного времени C++11, например,operator[] игнорируются, то корова для basic_string может быть технически осуществимым, но трудным для реализации.

операции, которые могут получить доступ к содержимому строки без копирования данных коровы, включают:

  • конкатенация через +.
  • выход через <<.
  • с помощью basic_string как аргумент стандартных библиотечных функций.

последнее, потому что стандартной библиотеке разрешено полагаться на конкретные знания и конструкции реализации.

кроме того, реализация может предложить различные нестандартные функции для доступа к содержимому строк без запуска копирования данных коровы.

основным осложняющим фактором является то, что в C++11 basic_string доступ к элементу должен инициировать данные копирование (un-sharing строковых данных) , но требуется для не бросать, например, C++11 §21.4.5/3 "Броски: ничего.". И поэтому он не может использовать обычное динамическое распределение для создания нового буфера для копирования данных коровы. Один из способов обойти это-использовать специальную кучу, где память может быть резерв без фактического выделения, а затем зарезервировать требуемую сумму для каждой логической ссылки на строковое значение. Резервировать и ООН-резервировать в такой куче можно постоянное время, O (1), и выделение суммы, которая уже зарезервирована, может быть noexcept. Чтобы соответствовать требованиям стандарта, при таком подходе, по-видимому, потребуется одна такая специальная куча на основе резервирования для каждого отдельного распределителя.


Примечания:
1 The const item accessor запускает копирование данных коровы, потому что он позволяет клиентскому коду получить ссылку или указатель на данные, которых нет разрешено аннулировать путем последующего копирования данных, вызванного, например, не -const пункт доступа.