27

And how can I write my own array class to not need a default constructor for its elements? Right now, when I do the new [] to allocate space, I need a default constructor.

std::vector does not.

How do they do this magic?

Dan
  • 5,929
  • 6
  • 42
  • 52
anon
  • 41,035
  • 53
  • 197
  • 293

4 Answers4

33

std::vector doesn't need the default constructor because it never uses it. Every time it needs to construct an element, it does it by using the copy constructor, because every time it has something to copy: either existing vector element or an element you yourself supplied for copying through a method's parameter (explicitly or implicitly, by relying on a default argument)

You can write a class like that in exactly the same way: every time you need to construct a new element in your array, require the user to supply an element for copying. In this case constructing that original element becomes user's responsibility.

Every time it appears as if std::vector "requires" a default constructor from you, it simply means that somewhere you relied on a default argument of some of the vectors methods, i.e. it was you who tried to default-construct an element, not the vector. The vector itself, again, will never try to default-construct elements.

In order to avoid the default constructor requirement during memory allocation, standard library allocates raw uninitialized memory block and then immediately copy-constructs new elements in that raw memory block (which is something new[] cannot do). This functionality is incapsulated in std::allocator class. You can use std::allocator in your code as well, meaning that the "magic" is immediately available to you too.

Note: The above applies to pre-C++11 version of C++ language specification. C++11 changed a lot of things. And these changes do create situations in which std::vector can use default constructors internally.

Also it might be worth noting that even the original C++98 specification allowed implementations to use function overloading instead of default arguments in order to implement the standard library interface. This means that formally it is possible to have a valid C++98 implementation of std::vector that uses default constructors internally.

AnT stands with Russia
  • 312,472
  • 42
  • 525
  • 765
  • 3
    on the other hand, they do use some magic that is very related: the std::allocator object. – Mooing Duck Apr 21 '12 at 00:44
  • 1
    `std::vector::emplace_back()` does call the default constructor. In general `emplace_back(Args...)` calls the constructor with arguments `Args...` – Tim Kuipers Aug 13 '15 at 14:00
  • 6
    @Angelorf this answer was written before C++11 was released :) In fact C++11 changed the behaviour of `vector x(5)` - now that is specified as calling the default constructor in-place 5 times, whereas in C++03 it meant that you default-construct one `X` and then the vector uses copy-construction 5 times, and your default is destroyed after. – M.M Dec 21 '15 at 09:30
12

std::vector only requires the element to have a default constructor if you use it in a way which requires the default constructor. So this code (stolen from a deleted answer) won't compile, because X does not have a default ctor:

#include <vector>

struct X
{
  X(int) {}
};

int main(void)
{
  std::vector<X> x(1); // vector of length 1, second argument defaults to X() !!
  return 0;
}

But if you write main like this instead:

int main(void)
{
  std::vector<X> x; // make empty vector
  x.push_back(X(1));
  return 0;
}

Then it works fine.

Dan
  • 5,929
  • 6
  • 42
  • 52
  • 11
    The first version will not compile, because `std::vector x(1)` is a shorthand for `std::vector x(1, X())`. It is in fact *you* who's implictly using the default constructor, not `vector`. Default arguments are evaluated "on your side". – AnT stands with Russia Mar 04 '10 at 05:20
8

You could allocate a block of bytes, then use placement new to make new instance of T (your parametric type) via copy constructor (not default constructor of course) when new items are pushed to the vector's back. This will not allow to to make "a vector of N default-initialized Ts" (which std::vector can make - which is why it does need T to have a default constructor for this purpose), but you could make vectors that start empty and can have Ts pushed onto them.

Alex Martelli
  • 854,459
  • 170
  • 1,222
  • 1,395
  • How do I allocate the space in the first place? malloc? – anon Mar 04 '10 at 05:18
  • 2
    @anon: Take a look at how `vector` does it... it uses an allocator, for example `new_allocator`. In my (old) Cygwin installation it works like this: `{ return static_cast<_Tp*>(::operator new(__n * sizeof(_Tp))); }` Or `malloc_allocator` does it this way: `pointer __ret = static_cast<_Tp*>(malloc(__n * sizeof(_Tp)));` – Dan Mar 04 '10 at 05:34
1

For me the std::vector was requiring a default constructor for my class (say T) because I was calling resize() method of the vector, despite I was only calling the method to shrink the vector, but never to grow.

Serge Rogatch
  • 13,865
  • 7
  • 86
  • 158