c_str () против data (), когда дело доходит до возвращаемого типа - PullRequest
0 голосов
/ 27 ноября 2018

После C ++ 11 я думал о c_str() и data() эквивалентно .

C ++ 17 вводит перегрузку для последнего, которая возвращает непостояннуюуказатель ( ссылка , который, я не уверен, полностью ли он обновлен в C ++ 17):

const CharT* data() const;    (1)   
CharT* data();                (2)   (since C++17)

c_str() возвращает только постоянный указатель:

const CharT* c_str() const;

Почему различие этих двух методов в C ++ 17, особенно когда C ++ 11 сделал их однородными?Другими словами, почему только один метод получил перегрузку, а другой - нет?

Ответы [ 4 ]

0 голосов
/ 27 ноября 2018

Две функции-члена c_str и data из std :: string существуют из-за истории класса std :: string.

До C ++11, std :: string можно было бы реализовать как копирование при записи.Внутреннее представление не нуждалось в нулевом завершении хранимой строки.Функция-член c_str удостоверилась, что возвращенная строка была завершена нулем.Функция-член data simlpy вернула указатель на сохраненную строку, которая не обязательно заканчивалась нулем.- Чтобы быть уверенным, что изменения строки были замечены для включения копирования при записи, обе функции должны были возвращать указатель на постоянные данные.

Все это изменилось в C ++ 11 при копировании при записибольше не было разрешено для std :: string.Поскольку c_str все еще требовалось для доставки строки с нулевым символом в конце, ноль всегда добавляется к фактической сохраненной строке.В противном случае при вызове c_str может потребоваться изменить сохраненные данные, чтобы завершить нулевую строку, что сделает c_str неконстантной функцией.Поскольку data доставляет указатель на сохраненную строку, он обычно имеет ту же реализацию, что и c_str .Обе функции все еще существуют из-за обратной совместимости.

0 голосов
/ 27 ноября 2018

Это зависит только от семантики "что вы хотите с этим делать".Вообще говоря, std::string иногда используется как буферный вектор, т. Е. Как замена std::vector<char>.Это можно увидеть в boost::asio часто.Другими словами, это массив символов.

c_str(): строго означает, что вы ищете строку с нулевым символом в конце.В этом смысле вам никогда не следует изменять данные, и вам никогда не понадобится строка как неконстантная.

data(): вам может понадобиться информация внутри строки как данные буфера, и даже как не-данныеУст.Вам может понадобиться, а может и не понадобиться изменить данные, что вы можете сделать, если только это не связано с изменением длины строки.

0 голосов
/ 27 ноября 2018

Новая перегрузка была добавлена ​​ P0272R1 для C ++ 17.Ни в самой статье, ни в ссылках там не обсуждается, почему только data получил новые перегрузки, а c_str - нет.Мы можем только спекулировать на данный момент (если только люди, участвующие в обсуждении, не вмешиваются), но я хотел бы предложить следующие моменты для рассмотрения:

  • Дажепросто добавление перегрузки к data сломало некоторый код;сохранение этого изменения было способом минимизировать негативное влияние.

  • Функция c_str до сих пор была полностью идентична data и фактически является "устаревшей" возможностью для взаимодействия кодакоторый принимает "строку C", то есть неизменный массив символов с нулевым символом в конце.Поскольку вы всегда можете заменить c_str на data, нет особой причины добавлять этот устаревший интерфейс.

Я понимаю, что самой мотивацией для P0292R1 было то, что существует наследиеAPI, которые ошибочно или по причинам C принимают только изменяемые указатели, даже если они не изменяются.Тем не менее, я полагаю, что мы не хотим добавлять к уже массивному API-интерфейсу строки что-то совершенно необходимое.

Еще один момент: с C ++ 17 вы сейчас разрешено записать в нулевой терминатор, пока вы записываете значение ноль.(Раньше это был UB для записи чего-либо в нулевой терминатор.) Изменяемый c_str создавал бы еще одну точку входа в эту конкретную тонкость, и чем меньше у нас тонкостей, тем лучше.

0 голосов
/ 27 ноября 2018

Причина, по которой элемент data() получил перегрузку, объясняется в этой статье на open-std.org.

TL; DR бумаги : Добавлена ​​неконстантная .data() функция-член для std::string, чтобы улучшить единообразие в стандартной библиотеке и помочь разработчикам на C ++ написать правильный код.Это также удобно при вызове функции библиотеки C, которая не имеет квалификации const для своих параметров строки C.

Некоторые соответствующие отрывки из статьи:

Аннотация
Является ли отсутствие у std::string неконстантной .data() функции-члена надзором или намеренным дизайном, основанным на семантике до C ++ 11 std::string?В любом случае это отсутствие функциональности побуждает разработчиков использовать небезопасные альтернативы в нескольких законных сценариях.В этой статье говорится о добавлении неконстантной .data() функции-члена для std :: string, чтобы улучшить единообразие в стандартной библиотеке и помочь разработчикам на C ++ написать правильный код.

Use Cases * 1025В библиотеки *
C иногда включаются подпрограммы с параметрами char *.Одним из примеров является параметр lpCommandLine функции CreateProcess в Windows API.Поскольку data() член std::string является константой, его нельзя использовать для того, чтобы заставить объекты std :: string работать с параметром lpCommandLine.Разработчики испытывают желание использовать .front() вместо этого, как в следующем примере.

std::string programName;
// ...
if( CreateProcess( NULL, &programName.front(), /* etc. */ ) ) {
  // etc.
} else {
  // handle error
}

Обратите внимание, что когда programName пусто, выражение programName.front() вызывает неопределенное поведение.Временная пустая C-строка исправляет ошибку.

std::string programName;
// ...

if( !programName.empty() ) { 
  char emptyString[] = {'\0'};    
  if( CreateProcess( NULL, programName.empty() ? emptyString : &programName.front(), /* etc. */ ) ) {
    // etc.
  } else {
    // handle error
  }
}

Если бы был неконстантный .data() член, как в случае с std::vector, правильный код был бы простым.

std::string programName;
// ...
if( !programName.empty() ) {
  char emptyString[] = {'\0'};
  if( CreateProcess( NULL, programName.data(), /* etc. */ ) ) {
    // etc.
  } else {
    // handle error
  }
}

Неконстантная функция-член .data() std::string также удобна при вызове функции библиотеки C, которая не имеет квалификации const для своих параметров строки C.Это часто встречается в старых кодах и в тех, которые должны быть переносимы со старыми компиляторами C.

...