Эта проблема очень странная, у меня есть следующий код:
#include <iostream>
#include <thread>
#include <chrono>
#include <string>
#include <vector>
struct Data {
Data(const char * d)
:data(d){}
std::string data;
};
// Using these data to avoid SOO(aka small object optimize) ASAP
std::vector<Data> arr {"11111111111111111111111111111111111111111111111111111111",
"2222222222222222222222222222222222222222222222222222222",
"3333333333333333333333333333333333333333333333333333333",
"4444444444444444444444444444444444444444444444444444444",
"5555555555555555555555555555555555555555555555555555555"};
class Test {
public:
void test() {
// I get a valid iterator first
std::vector<Data>::iterator it = arr.begin();
// I run a thread to clear arr
std::thread([](){
std::this_thread::sleep_for(std::chrono::seconds(2));
arr.clear();
}).detach();
// sleep 3s to make following code run after thread
std::this_thread::sleep_for(std::chrono::seconds(3));
// I think it->data is a invalid operation, Howerver, it works :(
std::string res = it->data;
}
};
int main() {
Test test;
test.test();
std::this_thread::sleep_for(std::chrono::seconds(5));
return 0;
}
Этот случай показывает посещение итератора после вызова clear(), теоретически это недопустимая операция, но она работает, мне очень интересно, почему, почему, почему???
См. также Неопределенное, неопределенное и определяемое реализацией поведение
@MilesBudnek, поскольку вектор никогда не объявлялся изменчивым, компилятор может даже использовать его части, которые были кэшированы до clear
! Вы действительно не можете ни на что полагаться, когда возникает неопределенное поведение.
После вызова clear()
все итераторы недействительны. См. vector::clear(). Их использование является неопределенным поведением.
использование clear()
стирает все элементы и заставляет все последующие вызовы size()
возвращать ноль
однако это не меняет пропускную способность вектора
Обратите внимание, что поведение undefined может привести к странным вещам.
clear()
вызывает деструкторы содержимого вектора. Они необходимы только для освобождения любой динамической памяти, которой владеют объекты. Компиляторы/библиотеки отличаются чем-то еще. MSVC имеет тенденцию изменять строковые объекты, чтобы они имели нулевую длину. Вероятно, чтобы предотвратить сбой плохо написанного кода за счет ненужного генерирования кода. UB в любом случае, если используется.
Ответ неполный. Обратите внимание, что вы предполагаете, что код, создаваемый компилятором, является нормальным и, похоже, предполагает, что сон синхронизирует данные. Но на самом деле даже компилятору будет позволено дублировать этот вектор, потому что это UB. Кроме того, тот факт, что вектор очищен, без какого-либо ограждения или синхронизации не обязательно должен быть виден из других потоков. Возможно даже, что итератор, память и блок управления остались нетронутыми.
@doug все stls должны изменять содержимое строки, потому что строка определена для хранения строки c (с нулевым завершением). Поэтому по крайней мере первый символ должен быть заменен нулем.
@FabianKeßler, когда объект удален, нет абсолютно никаких ограничений на его прежнее содержимое. Сами струны никто не чистил.
@FabianKeßler Не так. См. этот Clang ничего не меняет, когда строковый объект уничтожается. MSVC, OTOH, изменяет его на то же, что и std::move
. Пустая трата кода для dtor. Код примера содержит технический UB, но соответствует дампу памяти.
@doug Я не должен отвечать на что-то, когда я устал. Почему-то я подумал, что вы имели в виду, что содержимое строк не изменяется после вызова clear
в строке.
Неопределенное поведение не определено. То, что вектор был очищен, не означает, что его память была освобождена. Даже если память была освобождена, данные все еще там, пока что-то еще не появится и не использует эту память.