Можно ли использовать Place-new и vector :: data () для замены элементов в векторе?

Существует два существующих вопроса о замене векторных элементов, которые нельзя назначить:

Типичная причина того, что объект не присваивается, состоит в том, что его определение класса включает const члены и, следовательно, имеет свои operator= удален.

std::vector требует, чтобы его тип элемента был назначаемым. И действительно, по крайней мере, с помощью GCC, ни прямого назначения (vec[i] = x;), ни сочетание erase() а также insert() заменить элемент работает, когда объект не может быть назначен.

Может ли функция, подобная следующей, которая использует vector::data(), прямое уничтожение элемента и размещение нового с помощью конструктора копирования, будут использоваться для замены элемента, не вызывая неопределенного поведения?

template <typename T>
inline void replace(std::vector<T> &vec, const size_t pos, const T& src)
{
T *p = vec.data() + pos;
p->~T();
new (p) T(src);
}

Пример используемой функции приведен ниже. Это компилируется в GCC 4.7 и, кажется, работает.

struct A
{
const int _i;
A(const int &i):_i(i) {}
};

int main() {
std::vector<A> vec;
A c1(1);
A c2(2);

vec.push_back(c1);
std::cout << vec[0]._i << std::endl;

/* To replace the element in the vector
we cannot use this: */
//vec[0] = c2;

/* Nor this: */
//vec.erase(begin(vec));
//vec.insert(begin(vec),c2);

/* But this we can: */
replace(vec,0,c2);
std::cout << vec[0]._i << std::endl;

return 0;
}

15

Решение

Это недопустимо, потому что 3.8p7, который описывает использование вызова деструктора и размещение нового для воссоздания объекта на месте, определяет ограничения на типы элементов данных:

3.8 Время жизни объекта [basic.life]

7 — Если по истечении времени жизни объекта и до повторного использования или освобождения хранилища, в котором занятый объект, создается новый объект в месте хранения, которое занимал исходный объект, указатель, указывающий на исходный объект [. ..] Можно
использоваться для манипулирования новым объектом, если: […] — тип исходного объекта […] не содержит никаких нестатических элементов данных чей тип является константным или тип ссылки […]

Так как ваш объект содержит элемент данных const, после вызова и размещения деструктора новый вектор внутреннего data указатель становится недействительным, когда используется для ссылки на первый элемент; Я думаю, что любое разумное чтение пришло бы к выводу, что то же самое относится и к другим элементам.

Основанием для этого является то, что оптимизатор имеет право предполагать, что элементы const и опорных данных соответственно не модифицированы или повторно установлены:

struct A { const int i; int &j; };
int foo() {
int x = 5;
std::vector<A> v{{4, x}};
bar(v);                      // opaque
return v[0].i + v[0].j;      // optimised to `return 9;`
}
14

Другие решения

Ответ @ ecatmur правильный на момент написания статьи. В C ++ 17 мы теперь получаем std::launder (РГ21 предложение P0137). Это было добавлено, чтобы сделать такие вещи, как std::optional работать с const члены среди других случаев. Пока вы помните launder (то есть очистить) доступ к вашей памяти, тогда это теперь будет работать без вызова неопределенного поведения.

2

По вопросам рекламы [email protected]