Понимание ошибки
Непредвиденное поведение возникает из-за особенностей устаревшей реализации std::string
. Старые версии GCC, реализованные std::string
с использованием семантики копирования при записи. Это умная идея, но она вызывает ошибки, подобные той, которую вы видите. Это означает, что GCC пытался определить std::string
так, чтобы внутренний строковый буфер копировался только в том случае, если новый std::string
был изменен. Например:
std::string A = "Hello, world";
std::string B = A; // No copy occurs (yet)
A[3] = '*'; // Copy occurs now because A got modified.
Однако, когда вы берете постоянный указатель, копирование не происходит, потому что библиотека предполагает, что строка не будет изменена с помощью этого указателя:
std::string A = "Hello, world";
std::string B = A;
std::string const& A_ref = A;
const_cast<char&>(A_ref[3]) = '*'; // No copy occurs (your bug)
Как вы заметили, семантика копирования при записи имеет тенденцию вызывать ошибки. Из-за этого, а также из-за того, что копирование строки довольно дешево (с учетом всех обстоятельств), реализация copy copy-on-write для std::string
была обесценена и удалена в GCC 5.
Так почему вы видите эту ошибку, если используете GCC 5? Вероятно, вы компилируете и связываете старую версию стандартной библиотеки C ++ (в которой копирование при записи по-прежнему является реализация std::string
). Это то, что вызывает у вас ошибку.
Проверьте, для какой версии стандартной библиотеки C ++ вы компилируете, и, если возможно, обновите свой компилятор.
Как я могу узнать, какую реализацию std::string
использует мой компилятор?
- Новая реализация GCC:
sizeof(std::string) == 32
(при компиляции для 64 бит)
- Старая реализация GCC:
sizeof(std::string) == 8
(при компиляции для 64 бит)
Если ваш компилятор использует старую реализацию std::string
, то sizeof(std::string)
совпадает с sizeof(char*)
, потому что std::string
реализован как указатель на блок памяти. Блок памяти - это тот, который фактически содержит такие вещи, как размер и емкость строки.
struct string { //Old data layout
size_t* _data;
size_t size() const {
return *(data - SIZE_OFFSET);
}
size_t capacity() const {
return *(data - CAPACITY_OFFSET);
}
char const* data() const {
return (char const*)_data;
}
};
С другой стороны, если вы используете более новую реализацию std::string
, тогда sizeof(std::string)
должно быть 32 байта (в 64-битных системах). Это связано с тем, что более новая реализация хранит размер и емкость строки в самом std::string
, а не в данных, на которые она указывает:
struct string { // New data layout
char* _data;
size_t _size;
size_t _capacity;
size_t _padding;
// ...
};
Что хорошего в новой реализации? Новая реализация имеет ряд преимуществ:
- Доступ к размеру и емкости может быть выполнен быстрее (поскольку оптимизатор с большей вероятностью сохранит их в регистрах или, по крайней мере, они будут в кеше)
- Поскольку
std::string
составляет 32 байта, мы можем воспользоваться оптимизацией малых строк. Оптимизация малых строк позволяет хранить строки длиной менее 16 символов в пространстве, обычно занимаемом _capacity
и _padding
. Это позволяет избежать выделения кучи и быстрее для большинства случаев использования.
Ниже мы видим, что GDB использует старую реализацию std::string
, потому что sizeof(std::string)
возвращает 8 байтов:
![введите описание изображения здесь](https://i.stack.imgur.com/6a5t1.png)
person
Alecto Irene Perez
schedule
28.05.2019
v
... - person Barry   schedule 29.05.2019const_cast
предназначен не для этого. - person Retired Ninja   schedule 29.05.2019const_cast
работает до тех пор, пока данные не были объявлены как const.std::string
не хранит свои данные как массив констант, насколько мне известно. - person Quimby   schedule 29.05.2019