В С++ 11 могут ли быть изменены символы в массиве, на которые указывает строка:: c_str()?

std::string::c_str() возвращает указатель на массив, содержащий последовательность символов с нулевым завершением (т.е. C-строку), представляющую текущее значение строкового объекта.

В С++ 98 требовалось, чтобы "программа не изменяла ни одного из символов в этой последовательности". Это было вызвано возвращением const char *.

IN С++ 11 "возвращаемый указатель указывает на внутренний массив, используемый в настоящее время строковым объектом для хранения символов, соответствующих его значению", и я считаю, что требование не изменять его содержимое было отброшено. Это правда?

Является ли этот код ОК на С++ 11?

#include<iostream>
#include<string>
#include<vector>
using namespace std;

std::vector<char> buf;

void some_func(char* s)
{
    s[0] = 'X'; //function modifies s[0]
    cout<<s<<endl;
}

int main()
{
    string myStr = "hello";
    buf.assign(myStr.begin(),myStr.end());
    buf.push_back('\0');
    char* d = buf.data();   //C++11
    //char* d = (&buf[0]);  //Above line for C++98
    some_func(d);   //OK in C++98
    some_func(const_cast<char*>(myStr.c_str())); //OK in C++11 ?
    //some_func(myStr.c_str());  //Does not compile in C++98 or C++11
    cout << myStr << endl;  //myStr has been modified
    return 0;
}

Ответы

Ответ 2

В С++ 11, да, ограничение для c_str() все еще действует. (Обратите внимание, что тип возврата const, поэтому для этой функции не требуется никакого специального ограничения. const_cast в вашей программе - большой красный флаг.)

Но что касается operator[], это, по-видимому, является следствием только редакционной ошибки. Из-за изменения препинания для С++ 14 вы можете его изменить. Таким образом, интерпретация до некоторой степени зависит от вас. Конечно, это так распространено, что никакая реализация библиотеки не посмеет сломать его.

С++ 11:

Возвращает: * (begin() + pos), если pos < size(), в противном случае ссылка на объект типа T со значением диаграмма(); ссылочное значение не должно быть изменено.

С++ 14:

Возвращает: * (begin() + pos), если pos < размер(). В противном случае возвращается ссылка на объект типа charT со значением charT(), где изменение объекта приводит к поведению undefined.

Вы можете передать c_str() как ссылку только для чтения на функцию, ожидающую строку C, точно так же, как предлагает ее подпись. Функция, ожидающая ссылки на чтение и запись, обычно ожидает заданный размер буфера и может изменять размер строки, записывая NUL внутри этого буфера, реализация которых std::string фактически не поддерживается. Если вы хотите это сделать, вам нужно resize строку включить ваш собственный терминатор NUL, а затем передать & s[0], который является ссылкой на чтение и запись, а затем resize снова удалить ваш терминатор NUL и передать ответственность за завершение обратно в библиотеку.

Ответ 3

Я бы сказал, что если c_str() возвращает a const char *, то это не нормально, даже если можно утверждать, что он является серым регионом адвокатом языка.

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

Кроме того, как отмечали другие комментаторы, существуют и другие способы сделать то же самое, что и не нарушать какие-либо контракты. Так что это определенно не подходит.

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

Ответ 4

Я проверил, что это в опубликованном стандарте С++ 11

Спасибо

что случилось с & myStr.front()?

string myStr = "hello";
char* p1 = const_cast<char*>(myStr.c_str());
char* p2 = &myStr.front();
p1[0] = 'Y';
p2[1] = 'Z';

Кажется, что указатели p1 и p2 точно совпадают. Поскольку "Программа не должна изменять какие-либо значения, хранящиеся в массиве символов", кажется, что последние две строки выше являются незаконными и, возможно, опасными.

На этом этапе, как я бы ответил на мой вопрос, было бы безопаснее скопировать исходный std::string в вектор, а затем передать указатель на новый массив на любую функцию, которая может изменить символы.

Я надеялся, что этот шаг больше не понадобится в С++ 11 по причинам, которые я дал в своем оригинальном посте.