5

This question is about the difference in semantics and performance of new[] int and new [] int() and the change from the first to the second wording possibly unintendedly created when adding perfect forwarding of ctor arguments to allocator_traits::construct(). This question does NOT concern the quite obvious issue that a default ctor is run on all new elements constructed by resize() of a vector.

To me clearing the elements of a vector of built in types at resize seems like a waste. But VS2012 is implemented so that resize(n) and as a consequence also the constructor with a count parameter actually sets the allocated array of values to 0.

Also I found support for this in the standard, (http://www.open-std.org/JTC1/SC22/WG21/docs/papers/2012/n3485.pdf) but I think it may be a mistake that it got there, as it relies on a recent clause involving perfect forwarding:

page 507:

template <class T, class... Args>
static void construct(Alloc& a, T* p, Args&&... args);

5 Effects: invokes

::new (static_cast<void*>(p)) T(std::forward<Args>(args)...).

and as new int() must set the value to 0 according to clause 11 on page 191 of the same document the wasteful implementation in vector is correct.

The question is whether the standard committe actually wanted the empty parameter pack of the construct call to cause a change in behaviour from default construction to value construction for basic types.

Bengt Gustafsson
  • 517
  • 4
  • 10
  • 1
    There is a way to create a `vector` of `int` in C++11 without having it initialize to 0. I'd show how in an answer but I can't because this question has been closed. I would put the answer in the duplicate question, but my answer doesn't address that question because it really isn't quite a duplicate of this one. I'd put the code in this comment, but it would be unreadable. If you ask a new question, and I can get to it before it gets closed as a duplicate, I'll show how to create such a vector in C++11. – Howard Hinnant Apr 09 '13 at 14:51
  • I would like to know that, but notice that using an allocator does not cut it as I have to pass the vector to a (non-templated) function taking a "vanilla" vector. – Bengt Gustafsson Apr 10 '13 at 13:30
  • 1
    Ok, an allocator was what I was going to show you. You couldn't do it with an allocator in C++03, but you can in C++11. – Howard Hinnant Apr 10 '13 at 15:02

2 Answers2

6

No, it's not a mistake.

The previous version of the standard had resize(n) create one element and then copy that to all the new positions in the vector. The new wording is deliberate to allow for types that are not copyable, but just constructible.

Bo Persson
  • 90,663
  • 31
  • 146
  • 203
  • What are the exact requirements for an object in container in C++11? (It used to be CopyConstructable and CopyAssignable, but that was before move semantics.) – James Kanze Apr 09 '13 at 11:17
  • I think it depends on the type of the container and the operations you want to perform. :-( For a vector, the minimum seems to be MoveConstructible and MoveAssignable. For node based containers we now also have EmplaceConstructible (which might be a weaker requirement). – Bo Persson Apr 09 '13 at 11:26
  • I had hoped that the new resize() overload without default parameter was intended to be able to construct vector without initializing the elements, but of course your usage is also of interest. – Bengt Gustafsson Apr 09 '13 at 11:41
  • 1
    @Bengt - Leaving the ints uninitialized would not be portable to (the very few) systems where ints have trap values. You also have the option to do a `reserve` to allocate the space and then add the values later. – Bo Persson Apr 09 '13 at 11:54
  • @BoPersson The problem with `reserve` and `push_back`, in the (admittedly very rare) cases where performance is an issue, is that using them is typically more expensive than creating the elements with an unnecessary initialization. – James Kanze Apr 09 '13 at 12:50
5

It is an invariant that a container never holds an uninitialized object. This means that anything which increases the size of the container must initialize all of the new elements somehow. This is a feature, not a defect—accessing uninitialized values is undefined behavior, and you couldn't reallocate (or insert) if some of the values were not initialized. E.g.:

std::vector<int> v;
v.resize( 20 );
v.insert( v.begin(), 1 );

The last line would result in undefined behavior if the resize hadn't initialized the elements it created.

Mankarse
  • 39,818
  • 11
  • 97
  • 141
James Kanze
  • 150,581
  • 18
  • 184
  • 329
  • And yet you can define `struct A {int a; A(){}};` and make a vector of those... – Mankarse Apr 09 '13 at 11:19
  • 1
    Not legally:-). It would be undefined behavior. But it's an interesting point: the compile _will_ generate a copy constructor, even though using it may result in undefined behavior. – James Kanze Apr 09 '13 at 11:20
  • The thing is that for int the default constructor is defined to do nothing. Of course resize() should call on the default constructor for the new elements but now there is no way to say that I don't care what the values are initially as I will write them before reading them. – Bengt Gustafsson Apr 09 '13 at 11:39
  • @BengtGustafsson You don't have to say it. If you will write them before reading them, then having them initialized to `0` doesn't hurt you. – James Kanze Apr 09 '13 at 12:48
  • It does hurt me performance-wise. C++ containers supposedly should be "as fast as arrays". I think this is a big problem. The point here is that the default ctor for int is defined to do nothing, while the "special" constructor invoked in some contexts if you use an empty trailing parenthesis sets the value to 0. – Bengt Gustafsson Apr 10 '13 at 09:59