Почему не std::string.data() предоставляет изменчивый char *?

В С++ 11 array, dynarray, string и vector все получили метод data, который:

Возвращает указатель на базовый массив, служащий в качестве хранилища элементов. Указатель таков, что диапазон [ data(); data() + size()) всегда является допустимым диапазоном, даже если контейнер пуст. [Источник]

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

T* vector<T>::data();
const T* vector<T>::data() const;

Все применимые контейнеры, кроме string, которые предоставляют только const версию:

const char* string::data() const;

Что здесь произошло? Почему string получил shortchanged, когда char* string::data() будет так полезен?

Ответ 1

Поддержка С++ 11 для базового буфера string становится возможной дополнительным требованием к basic_string:

Элементы a basic_string сохраняются смежно, т.е. для a basic_string s, &*(s.begin() + n) == &*s.begin() + n для любого n в [0, s.size()) или, что эквивалентно, указатель на s[0] может быть передан функциям, которые ожидают указатель на первый элемент массива CharT[].

Этот базовый буфер может быть получен различными способами, например: &s.front(), &s[0] или &*s.first() Но этот вопрос: Почему не было доступа к string базовому буфере, предоставленному в форма char* string::data()? Таким образом, устранение требования для вызывающего пользователя разыменовать возврат метода string.

Чтобы ответить на это, важно отметить, что T* array<T>::data() и T* vector<T>::data() были добавлением, необходимым для С++ 11. Никакие дополнительные требования не были предъявлены С++ 11 к другим смежным контейнерам, таким как deque и string. const char* string::data(), однако существовал до С++ 11 с требованием:

Возвращаемый массив не должен быть завершен с нулем.

Это означает, что string не был "заменен" в переходе С++ 11 на data accessors, он просто не был включен. Хотя есть естественные недостатки в реализации С++ 11, которые требуют записи непосредственно в базовый буфер string, настоящая потребность в методе char* string::data() на С++ 17, где data function not method может использоваться для доступа к базовому буферу контейнеров. Это ускоряет изменение, которое указывает Alper:

std::basic_string<charT>::data() возвращает a const charT* 21.4.7.1 [string.accessors]. Хотя этот метод удобен, он не совсем соответствует std::array<T>::data() 23.3.2.5 [array.data] или std::vector<T>::data() 23.3.6.4 [vector.data], оба из которых предоставляют две версии (которые возвращают T* или const T*). Дополнительный метод data() может быть добавлен в std::basic_string, который возвращает charT*, поэтому его можно использовать в аналогичных ситуациях, в которых могут использоваться std::array и std::vector. Без метода const data() std::basic_string должен обрабатываться специально в коде, который иначе не обращается к используемому типу контейнера.

В момент написания это актуальная проблема в Library Evolution Work Group.

Ответ 2

Я думаю, что это ограничение исходит из дней (до 2011 года), когда std::basic_string не нужно было хранить его внутренний буфер как непрерывный массив байтов.

В то время как всем остальным (std::vector и таким) приходилось хранить свои элементы как непрерывную последовательность по стандарту 2003 года; поэтому data может легко вернуть mutable T*, потому что не было проблем с итерациями и т.д.

Если std::basic_string должен был возвратить mutable char*, это означало бы, что вы можете относиться к этому char* как к действительной C-строковой и префиксной операциям C-string, например strcpy, которые легко превратятся в undefined поведение было строкой, не выделенной смежно.

стандарт С++ 11 добавил правило, которое basic_string должно быть реализовано как непрерывный массив байтов. Разумеется, вы можете обойти это, используя старый трюк &str[0].