Право на перезаписывание std::string нулевого терминатора?
В С++ 11 мы знаем, что std::string
гарантированно будет как смежным, так и завершающим нуль (или более педантичным, заканчивается на charT()
, который в случае char
является нулевым символом 0).
Существует такой API C, который мне нужно использовать, который заполняет строку указателем. Он записывает всю строку + нулевой ограничитель. В С++ 03 мне всегда приходилось использовать vector<char>
, потому что я не мог предположить, что string
был смежным или завершенным нулем. Но в С++ 11 (предполагая правильно соответствующий basic_string
класс, который по-прежнему сохраняется в некоторых стандартных библиотеках), я могу.
Или я могу? Когда я это сделаю:
std::string str(length);
Строка будет выделять length+1
байты, причем последний заполняется нулевым терминатором. Это хорошо. Но когда я передаю это API C, он напишет символы length+1
. Он собирается перезаписать нуль-терминатор.
По общему признанию, он собирается перезаписать нуль-терминатор нулевым символом. Коэффициенты хороши, что это сработает (действительно, я не могу себе представить, как он не может работать).
Но мне все равно, что "работает". Я хочу знать, согласно спецификации, нормально ли перезаписывать нулевой ограничитель нулевым символом?
Ответы
Ответ 1
LWG 2475 сделал это действительным, отредактировав спецификацию operator[](size())
(вставленный текст выделен полужирным шрифтом):
В противном случае возвращает ссылку на объект типа charT
со значением charT()
, где изменяется объект на любое значение, отличное от charT()
приводит к undefined поведению.
Ответ 2
К сожалению, это UB, если я правильно интерпретирую формулировку (в любом случае это не разрешено):
§21.4.5 [string.access] p2
Возвращает: *(begin() + pos)
, если pos < size()
, в противном случае ссылка на объект типа T
со значением charT()
; ссылочное значение не должно быть изменено.
(ошибка редакции, в которой говорится T
не charT
.)
.data()
и .c_str()
в основном указывают на operator[]
(§21.4.7.1 [string.accessors] p1
):
Возвращает: указатель p
такой, что p + i == &operator[](i)
для каждого i
в [0,size()]
.
Ответ 3
Согласно спецификации, перезаписывание завершающего NUL
должно быть undefined.
Итак, правильная вещь - выделить символы length+1
в строке, передать строковый буфер в C API, а затем resize()
вернуться к length
:
// "+ 1" to make room for the terminating NUL for the C API
std::string str(length + 1);
// Call the C API passing &str[0] to safely write to the string buffer
...
// Resize back to length
str.resize(length);
(FWIW, я попробовал "перезаписывать NUL" подход на MSVC10, и он отлично работает.)
Ответ 4
Я предполагаю, что n3092 больше не является текущим, а тем, что у меня есть. Раздел 21.4.5 позволяет получить доступ к одному элементу. Для этого требуется pos <= size(). Если pos < size(), то вы получите фактический элемент, в противном случае (например, если pos == size()), вы получите немодифицируемую ссылку.
Я думаю, что в отношении языка программирования вид доступа, который может изменить значение, считается модификацией, даже если новое значение совпадает с старым значением.
Имеет ли g++ библиотеку педантичных ссылок, на которую вы можете ссылаться?