c_str() против data(), когда дело доходит до возвращаемого типа
После С++ 11 я думал о c_str()
и data()
одинаково.
С++ 17 вводит перегрузку для последнего, которая возвращает неконстантный указатель (ссылка, в которой я не уверен, полностью ли он обновился по сравнению с С++ 17):
const CharT* data() const; (1)
CharT* data(); (2) (since C++17)
c_str()
возвращает только постоянный указатель:
const CharT* c_str() const;
Почему дифференциация этих двух методов в С++ 17, особенно когда С++ 11 был тем, который сделал их однородными? Другими словами, почему только один метод получил перегрузку, а другой нет?
Ответы
Ответ 1
Новая перегрузка была добавлена P0272R1 для С++ 17. Ни сама статья, ни ссылки в ней не обсуждают, почему только data
получили новые перегрузки, а c_str
- нет. Мы можем только спекулировать на этом этапе (если только люди, вовлеченные в дискуссию, не вмешиваются), но я хотел бы предложить следующие моменты для рассмотрения:
-
Даже простое добавление перегрузки к data
нарушает некоторый код; Сохранение этого изменения было способом минимизировать негативное влияние.
-
Функция c_str
до сих пор была полностью идентична data
и фактически является "устаревшим" средством для взаимодействия кода, который принимает "строку C", то есть неизменный массив символов с нулевым символом в конце. Поскольку вы всегда можете заменить c_str
data
, нет особой причины для добавления в этот устаревший интерфейс.
Я понимаю, что самой мотивацией для P0292R1 было то, что существуют устаревшие API, которые ошибочно или по причинам C принимают только изменяемые указатели, даже если они не мутируют. Тем не менее, я полагаю, что мы не хотим добавлять в цепочку и без того массивный API, что абсолютно необходимо.
Еще один момент: с С++ 17 вам теперь разрешено писать в нулевой терминатор, если вы пишете ноль. (Раньше это был UB для записи чего-либо в нулевой терминатор.) Изменяемый c_str
бы еще одну точку входа в эту конкретную тонкость, и чем меньше у нас тонкостей, тем лучше.
Ответ 2
Причина, по которой член data()
получил перегрузку, объясняется в этой статье на open-std.org.
TL; DR статьи: добавлена неконстантная функция-член .data()
для std::string
чтобы улучшить единообразие в стандартной библиотеке и помочь разработчикам C++ написать правильный код. Это также удобно при вызове функции C-библиотеки, которая не имеет const квалификации для своих параметров C-строки.
Некоторые соответствующие отрывки из статьи:
Аннотация
Является ли отсутствие в std::string
неконстантной функции-члена .data()
оплошностью или намеренным дизайном, основанным на семантике std::string
до C++ 11? В любом случае это отсутствие функциональности побуждает разработчиков использовать небезопасные альтернативы в нескольких допустимых сценариях. В этой статье говорится о добавлении неконстантной функции-члена .data()
для std :: string, чтобы улучшить единообразие в стандартной библиотеке и помочь разработчикам C++ написать правильный код.
Случаи применения
Библиотеки C иногда включают подпрограммы, которые имеют параметры char *. Одним из примеров является параметр lpCommandLine
функции CreateProcess
в Windows API. Поскольку элемент data()
в std::string
является const, его нельзя использовать для того, чтобы заставить объекты 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
функция-член .data() std::string
также удобна при вызове функции C-библиотеки, которая не имеет квалификации const для своих параметров C-строки. Это распространено в старых кодах и тех, которые должны быть переносимы со старыми компиляторами Си.
Ответ 3
Это зависит только от семантики "что вы хотите с ней делать". Вообще говоря, std::string
иногда используется как буферный вектор, т.е. Как замена std::vector<char>
. Это часто можно увидеть в boost::asio
. Другими словами, это массив символов.
c_str()
: строго означает, что вы ищете строку с нулевым символом в конце. В этом смысле вам никогда не следует изменять данные, и вам никогда не понадобится строка как неконстантная.
data()
: вам может понадобиться информация внутри строки как данные буфера, и даже как неконстантные. Вам может понадобиться, а может и не понадобиться изменить данные, что вы можете сделать, если это не связано с изменением длины строки.
Ответ 4
Две функции-члена c_str и данные std :: string существуют из-за истории класса std :: string.
До С++ 11 std :: string могла быть реализована как копирование при записи. Внутреннее представление не нуждалось в нулевом завершении сохраненной строки. Функция-член c_str убедилась, что возвращаемая строка завершена нулем. Функция-член data simlpy вернула указатель на сохраненную строку, которая не обязательно была завершена нулем. - Чтобы быть уверенным, что изменения строки были замечены для включения копирования при записи, обе функции должны были возвращать указатель на постоянные данные.
Все это изменилось в С++ 11, когда копирование при записи больше не было разрешено для std :: string. Так как c_str по- прежнему требовался для доставки строки с нулевым символом в конце, ноль всегда добавляется к фактической сохраненной строке. В противном случае вызову c_str может потребоваться изменить хранимые данные, чтобы завершить строку null, что сделает c_str неконстантной функцией. Поскольку данные доставляют указатель на сохраненную строку, они обычно имеют ту же реализацию, что и c_str. Обе функции все еще существуют из-за обратной совместимости.