In C++ there are few compelling reasons to use a C array over std::vector. One of those few compelling reasons, at least with C++03, was the fact that it is impossible to use a vector to allocate an uninitialized array of objects. The "fill" constructor for std::vector is:
vector(size_type count, const T& value = T())
Meaning that...
int* array = new array[1000000];
is likely to be much more efficient than:
std::vector<int> v(1000000);
...since the vector constructor will have to zero-initialize the array of integers. Thus, when working with a vector of PODs, there is no real equivalent to malloc; the best you can get is an equivalent to calloc.
C++11 seems to have changed this, with the concept of "value-initialization." In C++11, std::vector has a new constructor which takes a single size_type value, with no default argument. This "value-initializes" all elements in the vector. The C++11 standard distinguishes between "value-initialization" and "zero-initialization."
My understanding is that "value-initialization" is equivalent to calling the default constructor on T. If T is a POD type like int, then the default constructor simply creates an uninitialized integer. Thus, in C++11, explicit vector::vector(size_type count) is truly equivalent to malloc if T is a POD.
However, my understanding of this is based on the draft C++11 standard, rather than the final standard.
Question: Is my understanding correct here? Does explicit vector::vector(size_type count) provide an uninitialized array (similar to malloc) if T is a POD?
Question: Is my understanding correct here? Does explicit vector::vector(size_type count) provide an uninitialized array
(similar to malloc) if T is a POD?
No. There is a difference here between C++03 and C++11, but that isn't it. The difference is that in C++03, vector<T>(N) would default construct a T, and then make N copies of it to populate the vector.
Whereas in C++11, vector<T>(N) will populate the vector by default constructing T N times. For POD types the effect is identical. Indeed, I would expect that for almost all types the effect is identical. However for something like a unique_ptr (a move-only type), the difference is critical. The C++03 semantics would never work since you can not make a copy of a move-only type.
So:
vector<unique_ptr<int>> v(10);
creates a vector of 10 null unique_ptrs (which are not copies of each other).
In the rare case that it makes a difference and you need the C++03 behavior that can easily be accomplished with:
vector<T> v(10, T());
Note: the value-initialization happens in the allocator, so if you want a vector to do default initialization instead of value initialization for default constructed elements, you can do something like:
template<typename T>
struct DefaultInitAllocator {
template<typename U>
void construct(U* p)
{ ::new (static_cast<void*>(p)) U; }
template<typename U, typename... Args>
void construct(U* p, Args&&... args)
{ ::new (static_cast<void*>(p)) U(std::forward<Args>(args)...); }
// ... rest of the allocator interface
};
// ...
typedef std::vector<int, DefaultInitAllocator<int>> DefaultInitVectorInt;
Related
It appears that in C++20, we're getting some additional utility functions for smart pointers, including:
template<class T> unique_ptr<T> make_unique_for_overwrite();
template<class T> unique_ptr<T> make_unique_for_overwrite(size_t n);
and the same for std::make_shared with std::shared_ptr. Why aren't the existing functions:
template<class T, class... Args> unique_ptr<T> make_unique(Args&&... args); // with empty Args
template<class T> unique_ptr<T> make_unique(size_t n);
enough? Don't the existing ones use the default constructor for the object?
Note: In earlier proposals of these functions, the name was make_unique_default_init().
These new functions are different:
Original make_XYZ: Always initializes the pointed-to value ("explicit initialization", see § class.expl.init in the standard).
New make_XYZ_for_overwrite: Performs "default initialization" of the pointed-to value (see § dcl.init, paragraph 7 in the standard); on typical machines, this means effectively no initialization for non-class, non-array types. (Yes, the term is a bit confusing; please read the paragraph at the link.)
This is a feature of plain vanilla pointers which was not available with the smart pointer utility functions: With regular pointers you can just allocate without actually initializing the pointed-to value:
new int
For unique/shared pointers you could only achieve this by wrapping an existing pointer, as in:
std::unique_ptr<int[]>(new int[n])
now we have a wrapper function for that.
Note: See the relevant ISO C++ WG21 proposal as well as this SO answer
allocate_shared, make_shared, and make_unique all initialize the underlying object by performning something equivalent to new T(args...). In the zero-argument case, that reduces to new T() - which is to say, it performs value initialization. Value initialization in many cases (including scalar types like int and char, arrays of them, and aggregates of them) performs zero initialization - which is to say, that is actual work being done to zero out a bunch of data.
Maybe you want that and that is important to your application, maybe you don't. From P1020R1, the paper that introduced the functions originally named make_unique_default_init, make_shared_default_init, and allocate_shared_default_init (these were renamed from meow_default_init to meow_for_overwrite during the national ballot commenting process for C++20):
It is not uncommon for arrays of built-in types such as unsigned char or double to be immediately initialized by the user in their entirety after allocation. In these cases, the value initialization performed by allocate_shared, make_shared, and make_unique is redundant and hurts performance, and a way to choose default initialization is needed.
That is, if you were writing code like:
auto buffer = std::make_unique<char[]>(100);
read_data_into(buffer.get());
The value initialization performed by make_unique, which would zero out those 100 bytes, is completely unnecessary since you're immediately overwriting it anyway.
The new meow_for_overwrite functions instead perform default initialization since the memory used will be immediately overwritten anyway (hence the name) - which is to say the equivalent of doing new T (without any parentheses or braces). Default initialization in those cases I mentioned earlier (like int and char, arrays of them, and aggregates of them) performs no initialization, which saves time.
For class types that have a user-provided default constructor, there is no difference between value initialization and default initialization: both would just invoke the default constructor. But for many other types, there can be a large difference.
As a followup to this question, the default allocator (std::allocator<T>) is required to implement construct as follows (according to [default.allocator]):
template <class U, class... Args>
void construct(U* p, Args&&... args);
Effects: ::new((void *)p) U(std::forward<Args>(args)...)
That is, always value-initialization. The result of this is that std::vector<POD> v(num), for any pod type, will value-initialize num elements - which is more expensive than default-initializing num elements.
Why didn't† std::allocator provide a default-initializing additional overload? That is, something like (borrowed from Casey):
template <class U>
void construct(U* p) noexcept(std::is_nothrow_default_constructible<U>::value)
{
::new(static_cast<void*>(p)) U;
}
Was there a reason to prefer value initialization in call cases? It seems surprising to me that this breaks the usual C++ rules where we only pay for what we want to use.
†I assume such a change is impossible going forward, given that currently std::vector<int> v(100) will give you 100 0s, but I'm wondering why that is the case... given that one could just as easily have required std::vector<int> v2(100, 0) in the same way that there are differences between new int[100] and new int[100]{}.
In C++03 Allocators construct member took two arguments: pointer and value which was used to perform copy-initialization:
20.1.6 Table 34
a.construct(p,t)
Effect:
::new((void*)p) T(t)
construct taking two parameters can be traced back to 1994 (pg. 18). As you can see, in orignal Stepanov concepts it wasn't part of allocator interface (it wasn't supposed to be configurable) and was present just as wrapper over placement new.
Only way to know for sure would to ask Stepanov himself, but I suppose that reason was following: if you want to construct something, you want to initialize it with specific value. And if you want your integers uninitializated, you can just omit construct call since it is not needed for POD types. Later construct and other related function were bundled into allocators and containers were parametrized on them introducing some loss of control on initialization for end user.
So it seems that lack of default initialization is for historical reasons: nobody though about its importance when C++ was standardized and later versions of the Standard would not introduce breaking change.
Reading the answer to this question, I was surprised to find that std::min(std::initializer_list<T>) takes its arguments by value.
If you use std::initializer_list in the way implied by its name, i.e. as an initializer to some object, I understand that we don't care about copying its elements since they will be copied anyway to initialize the object. However, in this case here we most likely don't need any copy, so it would seem much more reasonable to take the arguments as std::initializer_list<const T&> if only it were possible.
What's the best practice for this situation? Should you not call the initializer_list version of std::min if you care about not making unnecessary copies, or is there some other trick to avoid the copy?
There is no such thing as std::initializer_list<const T&>. Initializer lists can only hold objects by value.
See [dcl.init.list]/5:
An object of type std::initializer_list<E> is constructed from an initializer list as if the implementation allocated a temporary array of N elements of type const E, where N is the number of elements in the initializer list.
There are no arrays of references, so there cannot be an initializer_list of references either.
My suggestion in this circumstance would be to write a replacement for std::min which takes a variadic template of forwarding references. This works (although I'm sure it can be improved):
template<typename T, typename... Args>
T vmin( T arg1, Args&&... args )
{
T *p[] = { &arg1, &args... };
return **std::min_element( begin(p), end(p),
[](T *a, T *b) { return *a < *b; } );
}
See it working - using min or mmin, two extra copies are made (for a and b).
To sum up the comments:
An std::initializer_list is supposed to be a lightweight proxy as described on cppreference.com.
Therefore a copy of an initializer list should be very fast since the underlying elements are not copied:
C++11 §18.9/2
An object of type initializer_list<E> provides access to an array of objects of type const E.
[ Note:
A pair of pointers or a pointer plus a length would be obvious representations for initializer_list.
initializer_list is used to implement initializer lists as specified in 8.5.4. Copying an initializer list does not copy the underlying elements. — end note ]
Using a reference though would boil down to using a pointer and therefore an additional indirection.
The actual problem of std::min therefore is not that it takes the initializer_list by value, but rather, that the arguments to initializer_list have to be copied if they are computed at runtime. [1]
That the benchmark at [1] was broken as found out later is unfortunate.
auto min_var = std::min({1, 2, 3, 4, 5}); // fast
auto vec = std::vector<int>{1, 2, 3, 4, 5};
min_var = std::min({vec[0], vec[1], vec[2], vec[3], vec[4]}); // slow
[1]: N2722 p. 2
I need to constantly allocate a shared array of unsigned char as raw buffer to hold data from a TCP stream then pass it to several other threads for processing. My question is, does boost::make_shared<T[]>(std::size_t) value initialize or default initialize the underlying array? The former has too much overhead because of the high frequency (about ten times per second). I tried looking at the source code, but there are too many helper classes to get a clear understanding of what it does under the hood.
From Boost: make_shared and allocate_shared for arrays:
template<typename U> // U = T[]
shared_ptr<U> make_shared_noinit(size_t size);
template<typename U, typename A> // U = T[]
shared_ptr<U> allocate_shared_noinit(const A& allocator, size_t size);
Description: These overloads do not perform any value initialization of elements.
template<typename U> // U = T[N]
shared_ptr<U> make_shared_noinit();
template<typename U, typename A> // U = T[N]
shared_ptr<U> allocate_shared_noinit(const A& allocator);
Description: These overloads of the utilities above are for a fixed size array.
From the docs:
Effects: Allocates memory suitable for an array of type T and size size and constructs an array of objects in it via the placement new expression new(pointer) T() or new(pointer) T(args...). allocate_shared uses a copy of allocator to allocate memory. If an exception is thrown, has no effect.
This is performing value initialization of the array, as it value initializes each of the elements one at a time.
Boost also provides a make_shared_noinit version of the make_shared function for arrays, which perform no initializations of the array. That might better suit your needs.
I like to know how things work and as such have been delving deep into the c++ standard library. Something occurred to me the other day.
It is required that containters (for example: std::vector<int, std::allocator<int> >) use the allocator specified for allocations. Specifically the standard says:
23.1.8
Copy constructors for all container types defined in this clause copy
an allocator argument from their respective first parameters. All
other constructors for these container types take an Allocator&
argument (20.1.5), an allocator whose value type is the same as the
container’s value type. A copy of this argument is used for any memory
allocation performed, by these constructors and by all member
functions, during the lifetime of each container object. In all
container types defined in this clause, the member get_allocator()
returns a copy of the Allocator object used to construct the
container.
Also later in the standard it says (in a few different spots but i'll pick one) things like this:
explicit deque(size_type n, const T& value = T(), const Allocator& = Allocator());
Effects: Constructs a deque with n copies of value,
using the specified allocator.
OK, so on to my question.
Let's take std::vector as an example, the natural and efficient way to implement something like:
vector<T, A>::vector(const vector& x)
might look something like this:
template <class T, class A>
vector<T, A>::vector(const vector& x) {
pointer p = alloc_.allocate(x.size());
std::uninitialized_copy(x.begin(), x.end(), p);
first_ = p;
last_ = p + x.size();
end_ = p + x.size();
}
specifically, we allocate some memory and then copy construct all the members in place. Not bothering to do something like new value_type[x.size()] because that would default construct the array only to overwrite it!.
but, this doesn't use the allocator to do the copy construction...
I could manually write a loop which does something like this:
while(first != last) {
alloc_.construct(&*dest++, *first++);
}
but that's a waste, it's nearly identical to std::uninitialized_copy, the only difference is that is uses the allocator instead of placement new.
So, would you consider it an oversight that the standard doesn't have the (seemingly obvious to me) set of functions like these:
template <class In, class For, class A>
For uninitialized_copy(In first, In last, For dest, A &a);
template <class In, class Size, class For, class A>
For uninitialized_copy_n(In first, Size count, For dest, A &a);
template <class For, class T, class A>
void uninitialized_fill(For first, For last, const T& x, A &a);
template <class For, class Size, class T, class A>
void uninitialized_fill_n(For first, Size count, const T& x, A &a);
I would imagine that these types of functions (even though they are trivial to implement manually... until you try to make them exception safe) would prove fairly useful if people want to implement there own containers and such and make efficient use of copy construction while using allocators.
Thoughts?
I'm not sure whether we could call it an "oversight", per se.
No, you can't provide your own allocator to these specialised algorithms. But then there are other things that the standard doesn't contain, either.
#MarkB identifies a very good reason that the standard shouldn't do this (that the range has no knowledge of the container's allocator). I'd go so far as to say it's just an inherent limitation.
You can always re-invent uninitialized_copy for your needs, knowing what the allocator should be. It's just a two-line for loop.
If these functions were free-functions, I can't see any way that the compiler could detect allocator mismatches since the allocator type isn't retained by the iterator. This in turn could result in a variety of hard-to-find problems.
Yes, I think it is a (big) oversight, because information about the allocator is lost otherwise. The allocator is, in the current protocols, the only one that knows how to exactly construct an object in memory.
Now Boost includes alloc_construct, alloc_destroy https://www.boost.org/doc/libs/1_72_0/libs/core/doc/html/core/alloc_construct.html
which at least can help a bit implementing generic versions of uninitialized_copy/fill/etc(Alloc a, ...).