When elements are default-inserted into an instance of std::vector<T>, they are value-initialized by default. I often work with multi-threaded high-performance codes, where such value-initialization might for large arrays represent an unacceptable sequential bottleneck.
The typical approach based on reserve() and push_back()/emplace_back() is of no use if in concurrent codes. I usually end up with one of the following options:
definition of an empty default constructor for T,
definition and usage of a custom allocator with empty construct() member function.
However, both solutions are far from being elegant and also have drawbacks. The former cannot be used for T being a POD type, such as double. The latter requires a given implementation of the C++ Standard Library to supoort the relatively new DefaultInsertable concept. Moreover, definition of a custom allocator is quite tedious.
Is there any chance that in the future of C++ there will be some straightforward way how to "turn off" this default-insertion/value-initialization?
UPDATE
Mayebe, I should've asked simply if it will be possible to avoid zero-initialization of default-inserted elements of a vector for arithmetic types.
Vector is poorly suited to your needs. It supports resizing and accidental copy, neither of which make sense in a multi-threaded environment.
Write a simple container:
template<class T,class Storage=std::aligned_storage_t<sizeof(T),alignof(T)>{
struct buffer{
static_assert(std::is_pod<T)::value, "pod only");
std::size_t count;
std::unique_ptr<Storage[]> storage;
};
Populate it with container-esque begin/end/front/size/[]/empty etc.
Make it move only.
Use rule of zero (with =default).
Give it a explicit buffer(std::size_t) ctor that creates its content uninitialized.
Mix in some span/array_view types, and this should be suitable for your needs.
Maybe have emplace(size_t,Args&&) which does placement new for you with {}.
Related
I'm learning the c++ STL and it came to my attention that while most functionalities supported by std::vector and std::array (contiguous storage) are marked with constexpr, that's not the case for std::deque and other non-contiguous storages.
So I spent some time doing some research, I found a proposal in 2019, Making std::deque constexpr, and std::deque still has not implemented constexpr for its methods.
My confusion is that std::array guarantees that its elements are stored on the stack; just like a normal C-style array, so it should be computed at compile time, but std::vector allocates memory on the heap so if it's evaluated at compile time, so is deque, right?
Thanks!
According to https://github.com/cplusplus/papers/issues/665 which keeps a log of the progression of the proposal through the standards committee process, there seem to have been some doubts whether a constexpr std::deque can be implemented without core language changes.
Unfortunately it doesn't say what the specific concern is. Probably some common implementation makes use of some language construct that specifically is not allowed in constant expressions or the implementation relies on some construct that is undefined behavior according to the standard. The latter is usually not a problem for the standard library, since it is not bound by the language rules and can make assumptions about the particular compiler's behavior. However in constant expressions core language undefined behavior is always a hard error and therefore such constructs might often not be usable in constant expression contexts without introducing magic compiler workarounds.
As mentioned in the linked github issue, there seem to also be some library facilities which need to have constexpr added to make this work as well.
Aside from such issues, generally, I don't think there is any reason to not make all containers and container adaptors constexpr-friendly now that std::allocator can be used in constant expressions. They probably just want to make sure that they can be properly implemented as constexpr first. My guess is that for the same reason only std::string and std::vector were done with C++20, since these are the simplest and most important allocator-aware containers to apply constexpr to. (std::array has been constexpr for longer since it doesn't require any dynamic allocations.)
Although, looking at the date of the last entry in the issue (as well as the accompanying issues for std::list, std::priority_queue, etc.) it seems to not have progressed in the last two years, maybe because the author of the proposal did not pursue it further, but I can't really tell.
In either case, when we say that std::vector (or other allocator-aware containers) are constexpr-friendly, this means something else than it does for e.g. std::array. You can declare a constexpr std::array variable and use it as you would use a const std::array variables, but you can't declare a constexpr std::vector variable (generally at all) and use it as you would a const std::vector.
What you can do however is use a std::vector variable in e.g. a constexpr function to do some calculations, as long as the variable is created after the evaluation of the constant expression starts and is destroyed before it ends. Currently this is not possible with e.g. a std::list which is not constexpr-friendly.
The reason for this is that the compiler will not actually allocate memory at compile-time for the container and then transfer it in some way into a runtime allocation (whether static or dynamic). Instead dynamic allocations at compile-time are separate from runtime ones and must be deallocated before the constant expression evaluation in which they were allocated ends.
In an answer to this SO question:
What is the equivalent of boost::variant in the C++ standard library?
it is mentioned that boost::variant and std::variant differ somewhat.
What are the differences, as far as someone using these classes is concerned?
What motivation did the committee express to adopt std::variant with these differences?
What should I watch out for when coding with either of these, to maintain maximum compatibility with switching to the other one?
(the motivation is using boost::variant in pre-C++17 code)
Assignment/emplacement behavior:
boost::variant may allocate memory when performing assignment into a live variant. There are a number of rules that govern when this can happen, so whether a boost::variant will allocate memory depends on the Ts it is instantiated with.
std::variant will never dynamically allocate memory. However, as a concession to the complex rules of C++ objects, if an assignment/emplacement throws, then the variant may enter the "valueless_by_exception" state. In this state, the variant cannot be visited, nor will any of the other functions for accessing a specific member work.
You can only enter this state if assignment/emplacement throws.
Boost.Variant includes recursive_variant, which allows a variant to contain itself. They're essentially special wrappers around a pointer to a boost::variant, but they are tied into the visitation machinery.
std::variant has no such helper type.
std::variant offers more use of post-C++11 features. For example:
It forwards the noexcept status of the special member functions of its constituent types.
It has variadic template-based in-place constructors and emplacement functions.
Defect resolutions applied to C++17 may mean that it will also forward trivial copyability of its types. That is, if all of the types are trivially copyable, then so too will variant<Ts>.
It seems the main point of contention regarding the design of a variant class has been what should happen when an assignment to the variant, which should upon completion destory the old value, throws an exception:
variant<std::string, MyClassWithThrowingDefaultCtor> v = "ABC";
v = MyClassWithThrowingDefaultCtor();
The options seem to be:
Prevent this by restricting the possible representable types to nothrow-move-constructible ones.
Keep the old value - but this requires double-buffers.
Construct the new value on the heap, store a pointer to it in the variant (so the variant itself is not garbled even on exception). This is, apparently, what boost::variant does.
Have a 'disengaged' state with no value for each variant, and go to that state on such failures.
Undefined behavior
Make the variant throw when trying to read its value after something like that happens
and if I'm not mistaken, the latter is what's been accepted.
This is summarized from the ISO C++ blog post by Axel Naumann from Nov 2015.
std::variant differs slightly from the boost::variant
std::variant is declared in the header file rather than in <boost.variant.hpp>
std::variant never ever allocates memory
std::variant is usable with constexpr
Instead of writing boost::get(&variable), you have to write std::get_if(&variable) for std::variant
std::variant can not recursively hold itself and misses some other advanced techniques
std::variant can in-place construct objects
std::variant has index() instead of which()
Can I assume that for any type T, the type std::list<T> will have a same, constant size? Just to make it clear, I mean the size of the 'main' type itself only, not of the memory allocated by it.
It seems logical to me to assume that the size of T itself should only affect the size of list nodes allocated using the allocator.
However, there are two things that might cause the variancy of sizeof(std::list<T>) I can think of:
A standard C++ library trying to 'optimize' the std::list type by putting some of T instances into the std::list<T> itself. That seems like a bad idea to me, and it probably breaks the 'constant time' requirements put by the standard;
A standard C++ having library specializations of std::list<T> for some types, with the specializations having different sizes.
I can't think of any uses for (1) or (2) but I may be wrong.
What I'm thinking of achieving is using a fixed-size slice allocator for a template class usingstd::list<T> inside.
As a note: this is about variance for different types of T, and not for different allocators. I will have an explicit control of all instantiations, and they all will use the std::allocator.
Yes, it can.
The standard does not precise any guarantee on the size of a list<T> object as far as I am aware. Still, there are other guarantees.
Let us debunk some things:
A standard C++ library trying to 'optimize' the std::list type by putting some of T instances into the std::list<T> itself. That seems like a bad idea to me, and it probably breaks the 'constant time' requirements put by the standard;
It does not break the 'constant time' requirement in any way as long as this number is bounded since O(5) is O(1). However, during a splice operations, the nodes should move from one list to another without moving in memory. Thus local storage is prohibited.
A standard C++ having library specializations of std::list<T> for some types, with the specializations having different sizes.
Since we already excluded the possibility of local storage, it is hard to imagine any other variations of the base std::list<T> type by itself.
And let us worry about what matters:
The memory allocated by a std::list<T> is provided by its second template argument: the allocator. Most allocators (including std::allocator<T> which is the default) use a simple pointer type: T*... however an allocator should feel free to change this type.
Therefore, by changing the allocator parameter (and more precisely, its pointer type), one will naturally change the size of the std::list container in all the implementations I have seen of it.
Note that the allocator itself could be specialized for some types, however with the rebind magic it's a bit more difficult to achieve.
Chapter 23 of the C++11 standard (Containers) does not say anything about their sizes, so technically you cannot assume that the size will be constant. Implementors could (in theory) specialize some container for a specific primitive in a way that affects its footprint.
In practice, you could use a static assertion that sizeof(list<T>) == sizeof(list<int>) and use sizeof(list<int>) as the "fixed" size. Worst thing that can happen is a compile-time error.
Fixed-size slice allocator for class using std::list<T> makes little sense unless you also use a fixed-size slice allocator for the list itself there will probably be many more allocations for the list items than of the list headers.
Now you certainly can provide your own fixed-size slice allocator to the lists using their second template argument, the allocator. However there is a twist: You don't know how big the slice should be. A list<T> accepts allocator<T>, but what it really needs is allocator<U> where U is a structure containing the prev/next pointers and T. To get it it calls method rebind, that has signature template <typename T> template <typename T> allocator<U> allocator<T>::rebind<U>().
Since I actually needed to do that once, what I did was to create an object, that holds collection of allocators for several slice sizes. The allocator itself than held a shared pointer to this and a shared pointer to the allocator for size of it's argument. And on rebind I pulled appropriate allocator from the collection, or created one if it didn't exist. If you do this, it will as a side-effect solve allocation of the lists, because if there is more than one size, it will create separate pool for each. And it's not like there would be many different sizes, because the objects won't be big.
I'm currently learning about concurrency in C++ and came across using a vector of threads, which I believe will be possible in C++0x. However, my current compiler doesn't appear to have an implementation of move-aware containers and so I get errors generated because std::thread::thread(const std::thread&) is deleted, ie I can only use the move constructor/move assignment with std::thread.
Am I correct in thinking I could circumvent this issue by writing a custom allocator using
void MyAllocator::construct (pointer p, reference val)
/* should be non-const reference to val because using move constructor? */
{
new ((void*)p) T (std::move(val));
}
rather than
void allocator::construct (pointer p, const_reference val)
{
new ((void*)p) T (val);
}
? Or some other variation on this theme (possibly using an overload of MyAllocator::construct).
NB: This is mainly intended to be a short-term educational exercise and well enough performing work around to play around with threads in containers. I'd only be using MyAllocator in this context. However, please also point me at any libraries that may have this implemented so I can have a poke around the source.
If your compiler doesn't provide a move-aware std::vector then you'll have to write your own specialization of std::vector<std::thread> rather than just provide a custom allocator. The whole C++03 vector interface relies on copying: push_back() copies elements in; resize() initializes the empty elements with a copy of the element passed as the second parameter (even if that is the default value of T()); resize(), reserve(), insert(), erase() and push_back() will copy elements if the vector needs reallocating, or elements otherwise need moving around, and so forth.
This is such a common problem that I've included such a specialization with my (commercial) just::thread implementation of std::thread.
The easiest way to circumvent the problem would be to allocate the threads on the heap and manipulate pointers to them.
Check the Boost Pointer Container library: boost::ptr_vector<std::thread> seems to me what you are looking for.
The requirement that std containers only take copyable objects has more to do with the C++03 container interfaces than it does with the allocator implementation.
For example
vector<T> b(100);
vector<T> a;
a=b;
assert(a==b);
The standard assures us a==b is true. However, if T were not copyable, then in the best case a=b will not compile, in the worst a=b is undefined. Furthermore,
a.push_back(T());
may cause a to allocate new space, and under the hood there are copies made to the new underlying storage from the old.
Furthermore, there is nothing in the C++03 standard that says an implementation actually has to call allocator.construct, and in fact many (gcc for example) do not.
The C++0x standard adds new member functions to the container interface for moveable types, and clarifies how things like operator= behave in their presence.
See www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2486.pdf
Why is that the single parameter constructor of std::list<T> requires T to be a default-constructible type? I mean the following code does not compile.
struct Foo { // does not have default constructor.
Foo (int i) {}
}
int main(void) {
std::list<Foo> l(10);
}
It seems possible to use the construct and destroy idioms as they have already done in the std::vector, albeit with more book-keeping the list class.
On a related note, why not have the capacity function in list? You can argue that such a function would pay memory allocation cost up-front and eliminate the overhead later on as you push_back objects. At least it will make the interfaces of two STL sequence containers slightly more consistent.
std::list doesn't have a capacity function because it makes no sense; it never has to resize like a vector does. It's capacity is only limited by the available memory, which is not easily determined.
From what you asked for, I think you actually want reserve(). That's a one-off for vector because it (badly) needs such a thing; there's no particular requirement to make all functions consistent across all STL containers, especially when they make little sense for others.
You can accomplish the same effect using a custom allocator. As Manuel suggested, look at boost.
There is no general requirement that the type be default constructible - it must be copyable and assignable. Your code does not work because you try to create a list of 10 items - they have to be constructed somehow and so the default constructor must be used - but only in this specific case. If you created an empty list and added to it, there would be no such requirement.
The same is true for other containers - try compiling the following:
#include <vector>
struct A {
A( int x ) : z(x) {}
int z;
};
std::vector <A> a(10);
Regarding the second part of your question, I'd just observe that consistency of interface was not a prime design criterion for the standard containers - there is no intention, for example, that one type of container is a "drop-in" replacement for another. There is a good discussion of this in items 1 and 2 of Scott Meyers' book "Effective STL".
Neil already answered the main question.
Also note that you need a default constructor when calling resize().
You can circumvent this by having a STL list of pointers to objects but I guess this already was obvious to you.
On a related note, why not have the
capacity function in list? You can
argue that such a function would pay
memory allocation cost up-front and
eliminate the overhead later on as you
push_back objects. At least it will
make the interfaces of two STL
sequence containers slightly more
consistent.
I guess the problem here is that STL lists allow cross-list splicing. If you want to allocate memory upfront, have a look at the Boost Pool Allocator.
The reason is that, when you construct a list of n elements (where n is the parameter you used in the constructor), the list fills its structure of n elements with copies of T().
See sgi stl documentation for list.
So your question is really "why not have reserve and capacity functions in list?"
The answer to that is that there's no reason to reserve memory in advance for list - adding new elements never requires a realloc & copy for existing elements, there's no requirement that the memory holding the contents of a list be contiguous, and iterators don't get invalidated when doing a list::push_back().
All of those are the reason for the existence of vector<>::reserve(), and having memory in reserve for new elements is why a vector<> will perform placement new into raw memory.
Why is that the single parameter
constructor of std::list requires T
to be a default-constructible type?
Because this contructor - creates list with elements (number which you pass as parameter). Value of each element will be default. Also you could use constructor with two parameters, for create list with elements which will be initialized with second element value.
On a related note, why not have the
capacity function in list?
It doesn't have sense, because cost of adding new elements to list is much little than in case with vector.
std::vector does not have such a
restriction. My question is why not
use the same techniques
(create/destroy idioms) in the
std::list as well?
It is not restriction. Because if you don't use such constructor, default initializer will not required. Same thing true for vector.