странное поведение std :: vector :: resize () с gcc 4.7.0

Я все еще смущен поведением std::vector::resize(), Рассмотрим следующий код (см. Также требования к типу для std :: vector<тип>)

struct A {
A() : X(0) { std::cerr<<" A::A(); this="<<this<<'\n'; }
A(A const&) { assert(0); }  // is required but doesn't fire in vector::resize
int X;
};

int main()
{
std::vector<A> a;
a.resize(4);        // would not compile without A::A(A const&) or A::A(A&&)
}

Без A::A(A const&) или же A::A(A&&)линия с a.resize(4); не компилируется Однако этот конструктор никогда не вызывается: assert(0) не стреляет! Кто-нибудь может мне это объяснить?

Моя интерпретация заключается в том, что наличие любого из этих конструкторов требуется магии шаблона allocator_traits<> (использован std::vector::resize()), но на самом деле никогда не называется. Однако зачем вам нужен метод, если вы его не вызываете?

2

Решение

Последняя редакция стандарта (n3376) говорит:

12 — Если size() < sz,
присоединяет sz - size() вставленные по умолчанию элементы в последовательность.
13 — Требуется: T должен быть MoveInsertable а также DefaultInsertable в *this,

Подразумевается, что MoveInsertable требуется для любого перераспределения, которое может произойти, в то время как DefaultInsertable требуется для фактического добавления. Таким образом, ваш конструктор копирования или перемещения будет запускаться только в том случае, если ваш вектор уже содержит элементы и его необходимо перераспределить.

Действительно, если мы напишем:

std::vector<A> a;
a.resize(1);
assert(!a.empty() && a.capacity() < 4);
a.resize(4);

затем конструктор копирования или перемещения A называется, и ваш утверждается срабатывает.

4

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

Чтобы resize вектор, существующие элементы должны быть помещены во вновь выделенный фрагмент памяти, если в векторе не было достаточно места для хранения элементов, требуемых новым размером. Это делается путем их копирования. Таким образом, вы должны иметь конструктор копирования для изменения размера вектора. В этом случае нет существующих элементов, поэтому конструктор копирования не вызывается. Но это все еще должно присутствовать.

3

В вашем примере, когда вы вызываете метод vector :: resize (), вместо конструктора копирования вызывается конструктор. Вот почему вы не видите срабатывания утверждения.

Что касается того, зачем вам нужен конструктор копирования (и конструктор перемещения, который вы не определили и не объявили), так это то, что типы шаблонов должны быть конструктивными для копирования и конструируемыми для перемещения. [container.requirements.general] / 15 определяет требования к типу контейнера:

— T is DefaultInsertable into X means that the following expression is well-formed: allocator_traits<A>::construct(m, p);
— An element of X is default-inserted if it is initialized by evaluation of the expression allocator_traits<A>::construct(m, p);
where p is the address of the uninitialized storage for the element allocated within X.
— T is CopyInsertable into X means that the following expression is well-formed: allocator_traits<A>::construct(m, p, v);
— T is MoveInsertable into X means that the following expression is well-formed: allocator_traits<A>::construct(m, p, rv);
— T is EmplaceConstructible into X from args , for zero or more arguments args, means that the following expression is well-formed: allocator_traits<A>::construct(m, p, args);
— T is Erasable from X means that the following expression is well-formed: allocator_traits<A>::destroy(m, p);
0