Ответ 1
3 Требуется: программа не должна изменять любые значения, хранящиеся в массиве символов.
Это требование все еще присутствует в отношении проекта n3337 (Рабочий проект, наиболее похожий на опубликованный стандарт С++ 11, - N3337)
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;
}
3 Требуется: программа не должна изменять любые значения, хранящиеся в массиве символов.
Это требование все еще присутствует в отношении проекта n3337 (Рабочий проект, наиболее похожий на опубликованный стандарт С++ 11, - N3337)
В С++ 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
и передать ответственность за завершение обратно в библиотеку.
Я бы сказал, что если c_str() возвращает a const char *
, то это не нормально, даже если можно утверждать, что он является серым регионом адвокатом языка.
Как я вижу, это просто. Подпись метода указывает, что возвращаемый указатель не должен использоваться для изменения чего-либо.
Кроме того, как отмечали другие комментаторы, существуют и другие способы сделать то же самое, что и не нарушать какие-либо контракты. Так что это определенно не подходит.
Тем не менее, Борлдейдер обнаружил, что язык все еще говорит, что это не так.
Я проверил, что это в опубликованном стандарте С++ 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 по причинам, которые я дал в своем оригинальном посте.