@ eerorika Казалось бы, ответ на вопрос, но я хотел бы дать некоторое представление о том, почему этот код все еще работает.
Для этого конкретного примера c, я бы сказал, что это не так много случай C ++ UB, а скорее случай неправильного использования API (API std::vector
). В зависимости от того, как реализован std::vector
, фрагмент кода может не привести к какой-либо C ++ UB.
Под капотом вектор может быть просто реализован как указатель mallo c d с определенной емкостью :
template <typename T>
class vector {
public:
// Methods ...
private:
T *buffer_; // internal buffer
size_t size_; // # of elements
size_t capacity_; // Actual size of the buffer
};
std::vector::clear()
не меняет емкость вектора . Таким образом, может быть так, что для реализации clear()
не изменяется ни емкость, ни буфер, но size_
просто устанавливается на 0. В этом случае дезинфицирующие средства не будут сообщать об зависшей ссылке с auto &first = v[0];
, поскольку (внутренне для вектор) он фактически указывает на действительную память mallo c d.
Это, возможно, приведет к некоторой UB, если во внутреннем буфере будет сделано больше вещей. Например:
#include <iostream>
#include <vector>
int main() {
auto v = std::vector<int>();
v.push_back(20);
auto &first = v[0];
auto vector_ref = &v;
vector_ref->clear();
vector_ref->shrink_to_fit(); // Suggest to vector that we want to take back some memory
std::cout << first;
}
Может shrink_to_fit
(но не гарантирует ) перераспределить внутренний векторный буфер и заставить first
быть висячей ссылкой. Компиляция с clang++ v8
и ASan может показать, что (если произойдет перемещение), мы будем обращаться к ранее свободной памяти в куче:
$ clang++ /tmp/test.cpp -std=c++17 -fsanitize=address
$ ./a.out
=================================================================
==228989==ERROR: AddressSanitizer: heap-use-after-free on address 0x602000000010 at pc 0x0000004fc531 bp 0x7fff8bfcfb70 sp 0x7fff8bfcfb68
... Rest of the error
Редактировать: в t ie Вернемся к первоначальному вопросу: дезинфицирующие средства и предупреждения для проверки UB полезны для проверки «Правильно ли я использую C ++?», но когда дело доходит до вопросов, касающихся классов или API, это больше вопрос «Правильно ли я использую этот API?» ?». Проверять, правильно ли вы используете API, зависит от самого API (например, индексирование с использованием operator[]
на самом деле не выполняет проверку границ , но at()
делает и бросил бы исключение.