Понимание ошибки
Неожиданное поведение возникает из-за причуд в устаревшей реализации 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)
Как вы заметили, семантика копирование при записи имеет тенденцию вызывать ошибки. Из-за этого и из-за того, что копирование строки довольно дешево (учитывая все обстоятельства), реализация * 109 * копирования реализации 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 байтов:
![enter image description here](https://i.stack.imgur.com/6a5t1.png)