strange behaviour of std::vector::resize() with gcc 4.7.0

前提是你 提交于 2019-12-12 17:30:23

问题


I'm still confused about the behaviour of std::vector::resize(). Consider the following code (see also type requirements for 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&&)
}

Without A::A(A const&) or A::A(A&&), the line with a.resize(4); doesn't compile. However, that constructor is never called: the assert(0) doesn't fire! Can somebody explain that to me?

My interpretation is that the presence of either of these constructors is required by the template magic of allocator_traits<> (used by std::vector::resize()), but is actually never called. However, why would you require the presence of a method if you're not calling it?


回答1:


The latest revision of the standard (n3376) says:

12 - If size() < sz, appends sz - size() default-inserted elements to the sequence.
13 - Requires: T shall be MoveInsertable and DefaultInsertable into *this.

The implication is that MoveInsertable is required for any reallocation that might occur, while DefaultInsertable is required for the actual appending. So your copy or move constructor will fire only if your vector already contains elements and needs to be reallocated.

Indeed, if we write:

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

then the copy- or move-constructor of A is called, and your assert is triggered.




回答2:


In order to resize a vector, existing elements must be placed into the newly-allocated chunk of memory if the vector didn't have enough space to hold the elements required by the new size. This is done by copy-constructing them. So you must have a copy constructor to resize a vector. In this case, there are no existing elements, so the copy constructor is not called. But it still must be present.




回答3:


In your example, when you call vector::resize() method, the constructor is called instead of the copy constructor. That is why you do not see assert being triggered.

As for why you need the copy-constructor (and move constructor, which you haven't defined and declared), is that the template types have to be Copy-constructable and move-constructable. [container.requirements.general]/15 defines the requirements for container's type :

— 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);


来源:https://stackoverflow.com/questions/12259771/strange-behaviour-of-stdvectorresize-with-gcc-4-7-0

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!