6

At [Value-Initialized Objects in C++11 and std::vector constructor, Channel72 asks,

Question: Is my understanding correct here? Does explicit std::vector(size_type count) provide an uninitialized array (similar to malloc) if T is a POD?

The answer is no.

My question is, "Okay then, what does?"

One of the responses, by Nevin, hints at answering my question. To clarify, my question is, Is there a way to use std::vector<double> without it gratuitously filling allocated memory with zeros or whatever?

I am not asking for workarounds, like starting the vector at zero size and using push_back(). That is not always possible, and besides, at this point I want to get it figured out for no other reason than I want to get it figured out.

I cannot get Nevin's suggestion, a custom allocator, to compile. VC++ 2017rc (Dinkum) complains in its usual inscrutable way. Something about std::_Wrap_alloc. Nevin's code is incomplete, and I probably do not know how to complete it. Before I saw his, I wrote my own custom allocator which seems to work, but I am not confident in my understanding enough to swear by it.

For the time I have spent puzzling over this, I could have written a less dogmatic replacement for std::vector, plus several chapters of the Great American Novel.

Community
  • 1
  • 1
Jive Dadson
  • 16,680
  • 9
  • 52
  • 65
  • 2
    See the notes in: http://en.cppreference.com/w/cpp/concept/DefaultInsertable - my reading is the is value initialized ie zeroed This is from constructor (3) in http://en.cppreference.com/w/cpp/container/vector/vector – Richard Critten Dec 08 '16 at 21:36
  • @paddy When I used to discuss this sort of thing on the phone with Bjarne, ca. 1985, the motto was "If you don't use it, you don't pay for it." Drop a bigger name than that if you can. – Jive Dadson Dec 08 '16 at 21:43
  • @paddy Defaults are dangerous because they can mask latent bugs. Oh the stories I could tell. It might be a good idea to fill the vector with NaN's during development. Zeros, never! – Jive Dadson Dec 08 '16 at 21:45
  • @Richard Critten - Well done, sir! I will add it as an answer. I was fiddling around in my allocator with testing whether the type was arithmetic. Too specific. I will post your code as an answer. – Jive Dadson Dec 08 '16 at 22:00
  • @RichardCritten I posted your code as an answer. Post a better one if you can find the time. I want to up-vote the one I posted, but the software thinks that's too vain, – Jive Dadson Dec 08 '16 at 22:12

3 Answers3

6

HOORAY! Richard Critten to the rescue! His comment under the question leads directly to the answer.

The zero-spewing culprit is the default allocator template, namely std::allocator. So we replace it, or modify it with an allocator adapter.

I tidied up the code a little, and expanded the comments. Bill, please feel free to post a more comprehensive answer. But the following does the trick very nicely.

// Allocator adapter
// Given an allocator A, (std::allocator by default), this adapter 
// will, when feasible, override A::construct() with a version that 
// employs default construction rather than value-initialization.
// "Feasible" means the object (U *ptr) is default-constructable and
// the default constructor cannot throw exceptions.
// 
// Thus it thwarts gratuitous initializations to zeros or whatever.

template <typename T, typename A = std::allocator<T>>
class default_init_allocator : public A {
    typedef std::allocator_traits<A> a_t;
public:
    // http://en.cppreference.com/w/cpp/language/using_declaration
    using A::A; // Inherit constructors from A

    template <typename U> struct rebind {
        using other =
            default_init_allocator
            <  U, typename a_t::template rebind_alloc<U>  >;
    };

    template <typename U>
    void construct(U* ptr)
        noexcept(std::is_nothrow_default_constructible<U>::value) {
        ::new(static_cast<void*>(ptr)) U;
    }

    template <typename U, typename...Args>
    void construct(U* ptr, Args&&... args) {
        a_t::construct(static_cast<A&>(*this),
            ptr, std::forward<Args>(args)...);
    }
};
Jive Dadson
  • 16,680
  • 9
  • 52
  • 65
  • It is interesting that the second default_init_allocator::construct(), which forwards to std::allocator (base-type) version of construct(), is necessary. The first version hides the base-type version even when SFINAE takes the first version out of the picture. I cannot quote chapter and verse as to why that happens, but I have verified that it does happen. – Jive Dadson Dec 09 '16 at 13:17
  • The line "using A::A;" is mysterious to me. Without it, the static_cast does not compile. What's up with that? – Jive Dadson Dec 09 '16 at 20:18
  • Ignore my previous "what's up with that?" I found wuwt at http://en.cppreference.com/w/cpp/language/using_declaration – Jive Dadson Dec 10 '16 at 01:34
  • This allocator adapter code is from @Casey's answer: https://stackoverflow.com/a/21028912/427158 – maxschlepzig Sep 21 '18 at 19:13
0

Haven't tried this, but from some googling seems one can do this by providing a custom Allocator to std::vector. See the last line from https://en.cppreference.com/w/cpp/named_req/DefaultInsertable

If value-initialization is undesirable, for example, if the object is of non- class type and zeroing out is not needed, it can be avoided by providing a custom Allocator::construct

maxschlepzig
  • 35,645
  • 14
  • 145
  • 182
Chintan
  • 374
  • 2
  • 9
-4

You could initialize the vector with an array that has the allocated memory.

Type* typeArray = new Type [size];
vector <Type> typeVec (typeArray, typeArray+size);

This would give you the allocated memory (it would be garbage though) and you would have to be careful in accessing this because it would allow you to call typeVec [n].typeVal and it would likely produce undefined behavior unless you actually initialized the value at n

Or if Type was a non-class type it the value at n would just be the value of whatever was left there

Jesse Laning
  • 490
  • 4
  • 12
  • 1
    Does this remove the initialization overhead, or simply replaces the initialization with zero by the initialization with the garbage contained in `typeArray`? – A.S.H Dec 08 '16 at 22:13