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

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

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()), но на самом деле никогда не вызывается. Однако зачем вам требовать наличия метода, если вы его не вызываете?


person Walter    schedule 04.09.2012    source источник
comment
Там нечего копировать? Редактировать При проверке утверждение убивает его для меня... Вы компилируете утверждение ( -DNDEBUG, если я правильно помню)?   -  person BoBTFish    schedule 04.09.2012
comment
На самом деле, я не компилировал с --std=c++0x. При этом я вижу, что нормальный ctor звонил 4 раза. Посмотрите здесь: en.cppreference.com/w/cpp/container/ вектор/изменить размер   -  person BoBTFish    schedule 04.09.2012


Ответы (3)


Последняя версия стандарта (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, и срабатывает ваше утверждение.

person ecatmur    schedule 04.09.2012

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

person David Schwartz    schedule 04.09.2012
comment
+1 за правильное рассуждение, хотя ответ экатмура точнее - person Walter; 04.09.2012

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

Что касается того, почему вам нужен конструктор копирования (и конструктор перемещения, который вы не определили и не объявили), заключается в том, что типы шаблонов должны быть копируемыми и перемещаемыми. [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);
person BЈовић    schedule 04.09.2012
comment
Я знал это, ты не отвечаешь на мой вопрос - person Walter; 04.09.2012
comment
@Walter Из вашего вопроса: Без A::A(A const&) или A::A(A&&) строка с a.resize(4); не компилируется. Однако этот конструктор никогда не вызывается: assert(0) не срабатывает! Кто-нибудь может мне это объяснить? - person BЈовић; 04.09.2012
comment
Я думаю, вы должны выделить свои правки, иначе комментарии кажутся странными. - person Walter; 04.09.2012