странно поведение на 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
Там няма нищо за копиране? Редактиране При проверка, assert го убива за мен... Компилираш ли assert ( -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 за правилното разсъждение, въпреки че отговорът на ecatmur е по-точен - 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