Reputation: 45414
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?
Upvotes: 2
Views: 487
Reputation: 64213
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);
Upvotes: 0
Reputation: 157324
The latest revision of the standard (n3376) says:
12 - If
size() < sz
, appendssz - size()
default-inserted elements to the sequence.
13 - Requires:T
shall beMoveInsertable
andDefaultInsertable
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.
Upvotes: 4
Reputation: 182753
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.
Upvotes: 3