12

I've read in several places that std::vector requires it's template argument to be default constructible. Today I just tried it with one of my classes that has a deleted default constructor, and to my surprise it seems to be working just fine (with std::vector's default constructor). Is this portable behavior, or is this an implementation detail of gcc's STL and should I assume vector to require it's template argument to be default constructible?

Cubic
  • 14,902
  • 5
  • 47
  • 92
  • 3
    I wonder what would be `v[0]` after `std::vector v(1);` – Vlad Aug 01 '12 at 16:07
  • I know it's weird, that's why I'm asking. – Cubic Aug 01 '12 at 16:09
  • 7
    Only *certain operations* requires default constructibility. @Vlad gave an example. If you stay away from those, all is well. – R. Martinho Fernandes Aug 01 '12 at 16:10
  • If that's the only problem with this, then I don't actually have any problems with this - I don't particularly need that constructor, and I'd have to jump through hoops to provide a reasonable default constructor. Which would still not be reasonable, because the instance wouldn't be capable of doing anything other than existing, everything else would throw an exception. – Cubic Aug 01 '12 at 16:12
  • 1
    Although it is not always better, consider storing pointers (preferably smart) if your object has problems with construction, destruction or copying. – Daniel Aug 01 '12 at 16:19
  • @Vlad: that would not compile. `std::vector` per se does not require a default constructor, but some of the operations do, in particular the constructor you are using takes a second argument by const-reference, although it is defaulted to `T()`. – David Rodríguez - dribeas Aug 01 '12 at 18:37
  • @David: I see this now, but at the beginning I was interpreting the OP's question as "std::vector works completely ok with non-default-constructible template parameter". Thank you anyway. – Vlad Aug 01 '12 at 22:33

4 Answers4

21

There are two vector<T> members that require a default constructible T in C++11:

explicit vector(size_type n);
void resize(size_type sz);

Nothing else does. So if you use these signatures, you need to have a default constructible type, else you do not.

Howard Hinnant
  • 206,506
  • 52
  • 449
  • 577
  • 7
    Also calling emplace_back() and emplace(const_iterator position) [using those specific signatures]. – Nevin Aug 01 '12 at 19:09
15

The requirement in C++03 is that types being stored in a container be CopyConstructible and Assignable (see §23.1 Container Requirements). However, in C++11 these requirements are relaxed, and tend to apply to the operations performed on the container. So a simple default construction has no requirements (see teble 96, §23.1 in C++11 standard).

As soon as you try to copy a vector, or insert elements into it, you will meet the CopyInsertable, CopyAssignable, EmplaceConstructible, MoveInsertable, MoveAssignable etc. requirements

juanchopanza
  • 223,364
  • 34
  • 402
  • 480
7

std::vector does not unconditionally require its elements type to be default-constructible.

The original specification of std::vector (C++98, C++03) never even attempts to default-construct its elements internally. All new elements are always copy-constructed from an object supplied "from outside" (by the calling code) as an argument. This means that every time you need default-constructed elements in your vector, it is your side of the code (the caller) that has to default-construct it and supply it to std::vector as the "original" element to be copied.

For example, when you do something like this in C++98

std::vector<some_type> v(42);
v.resize(64);

it actually expands into

std::vector<some_type> v(42, some_type(), allocator_type());
v.resize(64, some_type());

through the default argument mechanism. In other words, the default-constructed "original" element is supplied to vector's constructor by the calling code, not created internally by the vector.

C++11 changed that and now std::vector has methods that perform default construction of its elements internally. This still does not unconditionally require vector elements to be default-constructible. It just means that you need default-constructible elements to use those specific std::vector's methods.

AnT stands with Russia
  • 312,472
  • 42
  • 525
  • 765
  • Hm. What about `std::vector v(1);`? This was legal in C++03 as well and has to default-construct `v[0]`. – Vlad Aug 01 '12 at 16:57
  • 1
    @Vlad: The "classis" vector's constructor takes more than one parameter. You simply don't see them because the have default arguments. When you do `std::vector v(1)` it actually stands for `std::vector v(1, T(), A())` (the last argument is allocator). I.e. it is still *you*, not vector, who's default-constructing that `T()` and sending its to the constructor for copying. – AnT stands with Russia Aug 01 '12 at 16:59
  • Well, it's perhaps call-site, not the developer using the code, as he actually didn't default-construct anything. Anyway, it's nitpicking from my side. +1 for nice observation. – Vlad Aug 01 '12 at 17:03
  • 1
    @Vlad: By "the caller" I meant "the calling code", not the developer who wrote it. – AnT stands with Russia Aug 01 '12 at 17:09
  • But what would `some_type()` mean, other than a default constructed instance? – juanchopanza Aug 01 '12 at 17:19
  • 2
    @juanchopanza: It *is* a default constructed instance, as I said above. The point is that in pre-C++11 library the default constructed instance is *always created outside* and supplied from *outside*, as an argument. Default-construction is *never* performed by the vector internally. Meanwhile, in C++11 default-construction can be performed internally. – AnT stands with Russia Aug 01 '12 at 17:25
  • @AndreyT but without a default constructible element type, I cannot see how a call to vector(size_type) could actually work. – juanchopanza Aug 01 '12 at 17:33
  • 1
    @juanchopanza: What version of library specification are you talking about? The first part of my answer applies to C++98/C++03. There's no such thing as `vector(size_type)` in C++98/C++03. C++98/C++03 only has `vector(size_type, const T& = T(), const A& = A())`. And that's exactly what is used when you do `vector v(42)`. – AnT stands with Russia Aug 01 '12 at 17:44
  • @AndreyT I was talking about C++03. OK, so my question was how could `vector v(42)` work without a default constructible `T`? Sorry if I was unclear before. – juanchopanza Aug 01 '12 at 18:06
  • 1
    @juanchopanza: `vector v(42)` cannot work without default-constructible `T`. As I said above, in C++03 `vector v(42)` is interpreted by the compiler as `vector v(42, T(), A())`, which simply won't compile for non-default-constructible `T`. I'm not arguing with that. The only point I'm making about C++03 is that the default construction in this case is performed in the context of the calling code (arguments are always prepared by the calling code), not internally by the vector. In C++11 it would be done internally by the vector. – AnT stands with Russia Aug 01 '12 at 18:15
  • 1
    @AndreyT, an excellent answer, just one quibble: C++03 17.4.4.4 [lib.member.functions] means it is conforming for an implementation to define `vector::resize` as a pair of overloaded functions, one of which would default-construct an element internally (not at the call site), similarly for the `vector(size_type, T)` constructor -- so that was always allowed in C++03, so you cannot rely on the semantics you claim. – Jonathan Wakely Aug 01 '12 at 18:26
  • @Jonathan Wakely: Yes, thanks for this note. Good point. It is present in C++98 as well. In other words, C++11 implementation conforms to by C++98 requirements. – AnT stands with Russia Aug 01 '12 at 20:41
4

Well, templates are in some meaning weakly typed. That is, the missing default constructor won't be detected until your code calls the method where it's used, perhaps internally -- this will give a compile-time error.

However, unless you are not touching the methods which use the default constructor internally, you are "safe". However, I don't know which is the "safe" subset, and I suspect that it's not defined by the standard. Example: vector copying might use resize, which in turn might use default constructor.

Vlad
  • 35,022
  • 6
  • 77
  • 199
  • 4
    In C++03, the standard actually *requires* that the elements be `CopyConstructible` and `Assignable`. So the compiler is allowed to detect a missing copy constructor, for example. This changes in C++11. – juanchopanza Aug 01 '12 at 16:17
  • @juanchopanza: I wonder if the `vector` implementations of the major C++03-compatible compilers check these constraints at e.g. simple declaration: `std::vector* pv;`. (Have no old compiler at hand.) – Vlad Aug 01 '12 at 16:25
  • I cannot get it to fail using gcc 4.7 and a vector of boost:noncopyable-derived classes. – juanchopanza Aug 01 '12 at 16:48
  • They don't check the requirements, but using a type not fulfilling all requirements *will* break library code, and in different places for different compilers. – Bo Persson Aug 01 '12 at 16:50
  • 3
    It is not forbidden for a compiler to accept code that goes outside what is the minimum requirement. – Bo Persson Aug 01 '12 at 16:52
  • 1
    @juanchopanza: if the standard _requires_ `CopyConstructible` etc., I would expect that the compiler _must_ detect, not _may_. But well, the compiler writers know it better anyway. – Vlad Aug 01 '12 at 16:53
  • @BoPersson: but if the code happens to compile but fails to satisfy the requirements, does this technically mean UB? – Vlad Aug 01 '12 at 16:55
  • 1
    @Vlad - It probably just means *extremely non-portable*. Suppose you just create a `vector v;` and call `v.size()` on the empty vector. Is it bad if that compiles? – Bo Persson Aug 01 '12 at 17:01
  • @BoPersson: being an adherent of tyrannical discipline in code, I tend to say "yes". – Vlad Aug 01 '12 at 17:02
  • @Vlad I was expecting the same, so I find it strange that the requirement isn't strictly adhered to. Unless I am misreading these requirements, or have missed some caveats somewhere else in the standard. – juanchopanza Aug 01 '12 at 17:21
  • @Vlad, _"I wonder if the vector implementations of the major C++03-compatible compilers check these constraints"_ GCC with `-D_GLIBCXX_CONCEPT_CHECKS` will check some constraints, but it's not done by default. Another (not 100% reliable) way to check them is to explicitly instantiate `vector` and see if it works. – Jonathan Wakely Aug 01 '12 at 18:29