Является ли законным иметь указатель на зарезервированный векторный элемент?

Мне любопытно, является ли это законным:

std::vector<some_class_type> vec;
vec.reserve(10);
some_class_type* ptr = vec.data() + 3; // that object doesn't exist yet

Обратите внимание, что я не пытаюсь получить доступ к указанному значению.

Это то, что стандарт говорит о data(), но я не уверен, что это важно:

Возвращает: Указатель такой, что [data(),data() + size()) является действительным ассортимент. Для непустого вектора data() == &front().

Ответ 1

Приведенный вами пример не показывает непосредственного поведения undefined. Согласно стандарту, поскольку количество элементов, которые вы резервируете, больше, чем текущая емкость вектора, произойдет перераспределение. Поскольку распределение происходит в точке, где reserve вызывается, указатель, возвращаемый data(), сам по себе действителен.

23.3.6.3/2 (Акцент на мине)

Эффекты: директива, которая информирует вектор о планируемом изменении размера, чтобы он мог соответственно управлять распределением хранилища. После reserve(), capacity() больше или равно аргументу резерва, если происходит перераспределение; и равным предыдущему значению емкости() в противном случае. Перераспределение происходит в этой точке тогда и только тогда, когда текущая емкость меньше аргумента резерва(). Если исключение выбрано иначе, чем конструктор перемещения типа, не связанного с копированием, нет никаких эффектов.

Если вы пытаетесь разыменовать указатель до добавления достаточного количества элементов, где указатель находится за пределами data() + size(), или если вы добавляете больше, чем capacity(), то происходит поведение undefined.

Ответ 2

В большинстве реализаций STL a reserve пустого вектора приведет к перераспределению и гарантирует, что данные, на которые вы указали, принадлежат/управляются.

Расположение данных (значение указателя, возвращаемого data()), может измениться при изменении размера вектора. Конечно, удержание указателя само по себе является законным, разыгрывание его для чтения в то время, когда оно не инициализировано, конечно, undefined, а разыменование его после инициализации является законным, если вы можете гарантировать, что ваш вектор не будет изменяться, и как таковой диапазон вас выделено все еще в одном месте.

Приращение указателя на данные, которые были malloc 'd, в порядке. В этом примере вы выполняете арифметику указателя, чтобы удерживать указатель на данные, которые, как вам известно, были выделены std::vector. Независимо от того, инициализирован ли элемент, на который указывает указатель, операция изменения размера является проблематичной, поскольку она может освободить память, на которую вы указываете.

Ответ 3

Определенно НЕТ: ваш указатель не может считаться действительным. Здесь доказательство того, что UB:

В стандарте говорится о величине (23.3.6.3), что reserve() имеет следующий эффект:

Директива, в которой сообщает вектор запланированного изменения размера, поэтому что он может соответствующим образом управлять распределением хранилища. После reserve(), capacity() больше или равно аргументу резерва если происходит перераспределение; и равна предыдущему значению емкости() в противном случае. Перераспределение происходит в этой точке тогда и только тогда, когда текущая емкость меньше аргумента reserve().

Таким образом, стандарт гарантирует, что если бы что-то было выделено и не было достаточной емкости, в этот момент должно произойти перераспределение. Но ничего больше.

Эта формулировка позволяет реализации управлять пустыми векторами другими способами. Например, вполне можно представить себе, что реализация может не выделять память для пустого вектора, который только что был создан, и выделять необходимую емкость только при добавлении первого элемента (стратегия "ленивого распределения" ).

В этом случае ваш пример приведет к указанию на неверный адрес.

Важное изменение: Некоторые могут утверждать, что условие "только если" будет гарантировать, что распределение должно иметь место в примере, потому что новая емкость будет больше первоначальной. Тем не менее, стандарт не претендует на начальную емкость вектора. Реализация, которая будет использовать стратегию локального размещения, ориентированную на блок (то есть управление пропускной способностью блоков minimu, например 10 элементов), будет соответствовать стандарту и привести к тому, что ваш пример указывает на неверный адрес, как описано выше.