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 0
s, 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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With