Something between std::array and std::vector [duplicate] - c++

As my usually used C++ compilers allow variable-length arrays (e.g. arrays depending on runtime size), I wonder if there is something like std::array with variable size? Of course std::vector is of variable size, but it allocates on heap, and reallocates on need.
I like to have a stack allocated array with size defined at runtime. Is there any std-template that may feature this? Maybe using std::vector with a fixed maximum size?

There are two proposals currently being worked on to bring run-time fixed size arrays to C++ which may be of interest to you:
Runtime-sized arrays with automatic storage duration. This would make runtime sized arrays a language feature (like in C11). So you could do:
void foo(std::size_t size) {
int arr[size];
}
C++ Dynamic Arrays. This would bring a new container to the library, std::dynarray, which is given a fixed size at construction. It is intended to be optimized to be allocated on the stack when possible.
void foo(std::size_t size) {
std::dynarray<int> arr(size);
}
These are both being worked on as part of an Array Extensions Technical Specification, which will be released alongside C++14.
UPDATE: std::dynarray is not implemented yet(25Aug2021).please refer to What is the status on dynarrays?

As Daniel stated in the comment, size of the std::array is specified as a template parameter, so it cannot be set during runtime.
You can though construct std::vector by passing the minimum capacity through the constructor parameter:
#include <vector>
int main(int argc, char * argv[])
{
std::vector<int> a;
a.reserve(5);
std::cout << a.capacity() << "\n";
std::cout << a.size();
getchar();
}
But. Still vector's contents will be stored on the heap, not on the stack. The problem is, that compiler has to know, how much space should be allocated for the function prior to its execution, so it is simply not possible to store variable-length data on the stack.

Maybe using std::vector with a fixed maximal size?
If boost is allowed then boost::container::static_vector and boost::container::small_vector are the closest I can think of
boost::container::static_vector<int, 1024> my_array;
boost::container::small_vector<int, 1024> my_vector;
static_vector is a sequence container like boost::container::vector with contiguous storage that can change in size, along with the static allocation, low overhead, and fixed capacity of boost::array.
The size of each object is still fixed but it can be worth it if the number of allocations is significant and/or the item count is small
If the vector can grow beyond the limit then just use boost::container::small_vector. The heap is only touched when the size is larger than the defined limit
small_vector is a vector-like container optimized for the case when it contains few elements. It contains some preallocated elements in-place, which can avoid the use of dynamic storage allocation when the actual number of elements is below that preallocated threshold.
If you use Qt then QVarLengthArray is another way to go:
QVarLengthArray is an attempt to work around this gap in the C++ language. It allocates a certain number of elements on the stack, and if you resize the array to a larger size, it automatically uses the heap instead. Stack allocation has the advantage that it is much faster than heap allocation.
Example:
int myfunc(int n)
{
QVarLengthArray<int, 1024> array(n + 1);
...
return array[n];
}
Some other similar solutions:
llvm::SmallVector
Facebook's folly::small_vector
Electronic Arts Standard Template Library's eastl::fixed_vector
If a 3rd party solution isn't allowed then you can roll your own solution by wrapping std::array in a struct to get a static vector
template<typename T, size_t N>
struct my_static_vector
{
explicit static_vector(size_t size) { } // ...
size_t size() const noexcept { return curr_size; }
static size_t capacity() const noexcept { return N; }
T& operator[](size_t pos) { return data[pos]; }
void push_back(const T& value) { data[curr_size++] = value; }
// ...
private:
std::array<typename T, N> data;
std::size_t curr_size;
}
And if small_vector is required then you can use std::variant to contain both the my_static_vector and the vector
template<typename T, size_t N>
struct my_small_vector
{
explicit small_vector(size_t size) { } // ...
size_t size() const noexcept {
if (data.index() == 0) {
return data.get<0>().size();
} else {
return data.get<1>().size();
}
}
static size_t capacity() const noexcept {
if (data.index() == 0) {
return data.get<0>().capacity();
} else {
return data.get<1>().capacity();
}
}
// ...
private:
std::variant<my_static_vector<T, N>, std::vector<T>> data;
}

Related

How to fill in a C++ container with fixed-size from vector with size defined at run-time?

Background: this is a followup to #pari's answer to Constant-sized vector.
My type, metadata_t does not have a default constructor.
I use std::make_unique for fixed-size arrays that the size is not available in compile-time. (ie const size).
typedef std::unique_ptr<metadata_t[]> fixedsize_metadata_t;
fixedsize_metadata_t consolidate(const std::vector<metadata_t> &array) {
// note: this is run-time:
auto n = array.size();
return fixedsize_side_metadata_t(array.begin(), array.end()); // error
return fixedsize_side_metadata_t(array); // error
return std::unique_ptr<metadata_t[]>(0); // no error, but not useful
return std::unique_ptr<metadata_t[]>(n); // error
}
However, the constructor of unique_ptr<...[]> only accepts a size (integer). How do I initialise and copy my vector into my unique_ptr<[]>?
I tried std::unique_ptr<metadata_t[]>(array.size()); to prepare and then copy/populate the contents in the next step, but it shows a compile error.
Note: I use C++20 (or higher if it was available). Could make_unique_for_overwrite be useful? ( C++23 ).
Note: At first I thought generate (as in this answer) can do it, but it does not solve my problem because n is a run-time information.
The size of my vector is determined at run-time.
The whole point of this function is to convert my std::vector into a fixed-size data structure (with run-time size).
The data structure does not have to be unique_ptr<T[]>.
The old title referred to unique_ptr but I am really looking for a solution for a fixed-size data structure. So far, it is the only data structure I found as a "constant-size indexed container with size defined at runtime".
You can't initialize the elements of a unique_ptr<T[]> array with the elements of a vector<T> array when constructing the new array (UPDATE: apparently you can, but it is still not going to be a solution solved with a single statement, as you are trying to do).
You will have to allocate the T[] array first, and then copy the vector's elements into that array one at a time, eg:
typedef std::unique_ptr<metadata_t[]> fixedsize_metadata_t;
fixedsize_metadata_t consolidate(const std::vector<metadata_t> &array) {
fixedsize_metadata_t result = std::make_unique<metadata_t[]>(array.size());
std::copy(array.begin(), array.end(), result.get());
return result;
}
UPDATE: you updated your question to say that metadata_t does not have a default constructor. Well, that greatly complicates your situation.
The only way to create an array of objects that don't support default construction is to allocate an array of raw bytes of sufficient size and then use placement-new to construct the individual objects within those bytes. But now, you are having to manage not only the objects in the array, but also the byte array itself. By itself, unique_ptr<T[]> won't be able to free that byte array, so you would have to provide it with a custom deleter that frees the objects and the byte array. Which also means, you will have to keep track of how many objects are in the array (something new[] does for you so delete[] works, but you can't access that counter, so you will need your own), eg:
struct metadata_arr_deleter
{
void operator()(metadata_t *arr){
size_t count = *(reinterpret_cast<size_t*>(arr)-1);
for (size_t i = 0; i < count; ++i) {
arr[i]->~metadata_t();
}
delete[] reinterpret_cast<char*>(arr);
}
};
typedef std::unique_ptr<metadata_t[], metadata_arr_deleter> fixedsize_metadata_t;
fixedsize_metadata_t consolidate(const std::vector<metadata_t> &array) {
const auto n = array.size();
const size_t element_size = sizeof(std::aligned_storage_t<sizeof(metadata_t), alignof(metadata_t)>);
auto raw_bytes = std::make_unique<char[]>(sizeof(size_t) + (n * element_size));
size_t *ptr = reinterpret_cast<size_t*>(raw_bytes.get());
*ptr++ = n;
auto *uarr = reinterpret_cast<metadata_t*>(ptr);
size_t i = 0;
try {
for (i = 0; i < n; ++i) {
new (&uarr[i]) metadata_t(array[i]);
}
}
catch (...) {
for (size_t j = 0; j < i; ++j) {
uarr[j]->~metadata_t();
}
throw;
}
raw_bytes.release();
return fixedsize_metadata_t(uarr);
}
Needless to say, this puts much more responsibility on you to allocate and free memory correctly, and it is really just not worth the effort at this point. std::vector already supports everything you need. It can create an object array using a size known at runtime, and it can create non-default-constructable objects in that array, eg.
std::vector<metadata_t> consolidate(const std::vector<metadata_t> &array) {
auto n = array.size();
std::vector<metadata_t> varr;
varr.reserve(n);
for (const auto &elem : array) {
// either:
varr.push_back(elem);
// or:
varr.emplace_back(elem);
// it doesn't really matter in this case, since they
// will both copy-construct the new element in the array
// from the current element being iterated...
}
return varr;
}
Which is really just a less-efficient way of avoiding the vector's own copy constructor:
std::vector<metadata_t> consolidate(const std::vector<metadata_t> &array) {
return array; // will return a new copy of the array
}
The data structure does not have to be unique_ptr<T[]>. The old title referred to unique_ptr but I am really looking for a solution for a fixed-size data structure. So far, it is the only data structure I found as a "constant-size indexed container with size defined at runtime".
What you are looking for is exactly what std::vector already gives you. You just don't seem to realize it, or want to accept it. Both std::unique_ptr<T[]> and std::vector<T> hold a pointer to a dynamically-allocated array of a fixed size specified at runtime. It is just that std::vector offers more functionality than std::unique_ptr<T[]> does to manage that array (for instance, re-allocating the array to a different size). You don't have to use that extra functionality if you don't need it, but its base functionality will suit your needs just fine.
Initializing an array of non-default constructibles from a vector is tricky.
One way, if you know that your vector will never contain more than a certain amount of elements, could be to create an index_sequence covering all elements in the vector. There will be one instantiation of the template for each number of elements in your vector that you plan to support and the compilation time will be "silly".
Here I've selected the limit 512. It must have a limit, or else the compiler will spin in endless recursion until it gives up or crashes.
namespace detail {
template <class T, size_t... I>
auto helper(const std::vector<T>& v, std::index_sequence<I...>) {
if constexpr (sizeof...(I) > 512) { // max 512 elements in the vector.
return std::unique_ptr<T[]>{}; // return empty unique_ptr or throw
} else {
// some shortcuts to limit the depth of the call stack
if(sizeof...(I)+255 < v.size())
return helper(v, std::make_index_sequence<sizeof...(I)+256>{});
if(sizeof...(I)+127 < v.size())
return helper(v, std::make_index_sequence<sizeof...(I)+128>{});
if(sizeof...(I)+63 < v.size())
return helper(v, std::make_index_sequence<sizeof...(I)+64>{});
if(sizeof...(I)+31 < v.size())
return helper(v, std::make_index_sequence<sizeof...(I)+32>{});
if(sizeof...(I)+15 < v.size())
return helper(v, std::make_index_sequence<sizeof...(I)+16>{});
if(sizeof...(I)+7 < v.size())
return helper(v, std::make_index_sequence<sizeof...(I)+8>{});
if(sizeof...(I)+3 < v.size())
return helper(v, std::make_index_sequence<sizeof...(I)+4>{});
if(sizeof...(I)+1 < v.size())
return helper(v, std::make_index_sequence<sizeof...(I)+2>{});
if(sizeof...(I) < v.size())
return helper(v, std::make_index_sequence<sizeof...(I)+1>{});
// sizeof...(I) == v.size(), create the pointer:
return std::unique_ptr<T[]>(new T[sizeof...(I)]{v[I]...});
}
}
} // namespace detail
template <class T>
auto make_unique_from_vector(const std::vector<T>& v) {
return detail::helper(v, std::make_index_sequence<0>{});
}
You can then turn your vector into a std::unique_ptr<metadata_t[]>:
auto up = make_unique_from_vector(foos);
if(up) {
// all is good
}
Demo (compilation time may exceed the time limit)
You have to allocate some uninitialized memory for an array and copy construct the elements in-place using construct_at. You can then create a unique_ptr using the address of the constructed array:
#include <vector>
#include <memory>
struct metadata_t {
metadata_t(int) { }
};
typedef std::unique_ptr<metadata_t[]> fixedsize_metadata_t;
fixedsize_metadata_t consolidate(const std::vector<metadata_t> &array) {
// note: this is run-time:
auto n = array.size();
std::allocator<metadata_t> alloc;
metadata_t *t = alloc.allocate(n);
for (std::size_t i = 0; i < array.size(); ++i) {
std::construct_at(&t[i], array[i]);
}
return fixedsize_metadata_t(t);
}

Why can't we change data pointer of std::vector?

I have an array char* source and a vector std::vector<char> target. I'd like to make the vector target point to source in O(1), without copying the data.
Something along these lines:
#include <vector>
char* source = new char[3] { 1, 2, 3 };
std::vector<char> target;
target.resize(3);
target.setData(source); // <- Doesn't exist
// OR
std::swap(target.data(), source); // <- swap() does not support char*
delete[] source;
Why is it not possible to manually change where a vector points to? Is there some specific, unmanageable problem that would arise if this was possible?
C++ vector class supports adding and deleting elements, with guaranteed consecutive order in memory. If you could initialize your vector with existing memory buffer, and add enough elements to it, it would either overflow or require reallocation.
The interface of vector assumes that it manages its internal buffer, that is, it can allocate, deallocate, resize it whenever it wants (within spec, of course). If you need something that is not allowed to manage its buffer, you cannot use vector - use a different data structure or write one yourself.
You can create a vector object by copying your data (using a constructor with two pointers or assign), but this is obviously not what you want.
Alternatively, you can use string_view, which looks almost or maybe exactly what you need.
std::vector is considered to be the owner of the underlying buffer. You can change the buffer but this change causes allocation i.e. making a copy of the source buffer which you don't want (as stated in the question).
You could do the following:
#include <vector>
int main() {
char* source = new char[3] { 1, 2, 3 };
std::vector<char> target;
target.resize(3);
target.assign(source, source + 3);
delete[] source;
return 0;
}
but again std::vector::assign:
Replaces the contents with copies of those in the range [first, last).
So copy is performed again. You can't get away from it while using std::vector.
If you don't want to copy data, then you should use std::span from C++20 (or create your own span) or use std::string_view (which looks suitable for you since you have an array of chars).
1st option: Using std::string_view
Since you are limited to C++17, std::string_view might be perfect for you. It constructs a view of the first 3 characters of the character array starting with the element pointed by source.
#include <iostream>
#include <string_view>
int main() {
char* source = new char[3] { 1, 2, 3 };
std::string_view strv( source, 3 );
delete[] source;
return 0;
}
2nd option: Using std::span from C++20
std::span comes from C++20 so it might not be the most perfect way for you, but you might be interested in what it is and how it works. You can think of std::span as a bit generalized version of std::string_view because it is a contiguous sequence of objects of any type, not just characters. The usage is similar as with the std::string_view:
#include <span>
#include <iostream>
int main() {
char* source = new char[3] { 1, 2, 3 };
std::span s( source, 3 );
delete[] source;
return 0;
}
3rd option: Your own span
If you are limited to C++17, you can think of creating your own span struct. It might still be an overkill but let me show you (btw take a look at this more elaborated answer):
template<typename T>
class span {
T* ptr_;
std::size_t len_;
public:
span(T* ptr, std::size_t len) noexcept
: ptr_{ptr}, len_{len}
{}
T& operator[](int i) noexcept {
return *ptr_[i];
}
T const& operator[](int i) const noexcept {
return *ptr_[i];
}
std::size_t size() const noexcept {
return len_;
}
T* begin() noexcept {
return ptr_;
}
T* end() noexcept {
return ptr_ + len_;
}
};
int main() {
char* source = new char[3] { 1, 2, 3 };
span s( source, 3 );
delete[] source;
return 0;
}
So the usage is the same as with the C++20's version of std::span.
The std::string_view and std::span are good things to have (if you have compiler version supporting them). Rolling your own similars is ok too.
But some people miss the whole point why one would want to do this exactly to a vector:
Because you have an API that gives Struct[] + size_t and give you ownership
and you also have an API that accepts std::vector<Struct>
ownership could be easily transferred into the vector and no copies made!
You can say: But what about custom allocators, memory mapped file pointers, rom memory that I could then set as the pointer?
If you are already about to set vector internals you should know what you are doing.
You can try to supply a "correct" allocator in those cases to your vector actually.
Maybe give a warning on compiling this code yes, but it would be nice if it would be possible.
I would do it this way:
std::vector would get a constructor that asks for a std::vector::raw_source
std::vector::raw_source is an uint8_t*, size_t, bool struct (for now)
bool takeOwnership: tells if we are taking ownership (false => copy)
size_t size: the size of the raw data
uint8_t* ptr: the pointer to raw data
When taking ownership, vector resize and such uses the vectors allocation strategy as you otherwise provided with your template params anyways - nothing new here. If that does not fit the data you are doing wrong stuff!
Yes API I say look more complicated than a single "set_data(..)" or "own_memory(...)" function, but tried to make it clear that anyone who ever uses this api pretty much understands implications of it automagically. I would like this API to exists, but maybe I still overlook other causes of issues?

Memory size of the new allocated int in c++, Is there a different and better way to see it?

In this program I am trying to find out how much memory is allocated for my pointer. I can see it in this way that it should be 1 gibibyte which is = 1 073 741 824 bytes. My problem is that the only way I can get this thru is by taking the size of int which is 4 and multiplying by that const number. Is there a different way?
#include "stdafx.h"
#include <iostream>
#include <new>
int main(){
const int gib = 268435256; //Created a constant int so I could allocate 1
//Gib memory
int *ptr = new int [gib];
std::cout << sizeof (int)*gib << std::endl;
std::cout << *ptr << std::endl;
std::cout << ptr << std::endl;
try {
}catch (std::bad_alloc e) {
std::cerr << e.what() << std::endl;
}
system("PAUSE");
delete[] ptr;
return 0;
}
No, there is no way. The compiler internally adds information about how much memory was allocated and how many elements were created by new[], because otherwise it couldn't perform delete[] correctly. However, there is no portable way in C++ to get that information and use it directly.
So you have to store the size separately while you still know it.
Actually, you don't, because std::vector does it for you:
#include <iostream>
#include <vector>
#include <new>
int main() {
const int gib = 268435256;
try {
std::vector<int> v(gib);
std::cout << (v.capacity() * sizeof(int)) << '\n';
} catch (std::bad_alloc const& e) {
std::cerr << e.what() << '\n';
}
}
You should practically never use new[]. Use std::vector.
Note that I've used capacity and not size, because size tells you how many items the vector represents, and that number can be smaller than the number of elements supported by the vector's currently allocated memory.
There is also no way to avoid the sizeof, because the size of an int can vary among implementations. But that's not a problem, either, because a std::vector cannot lose its type information, so you always know how big one element is.
You wouldn't need the multiplication if it was a std::vector<char>, a std::vector<unsigned char> or a std::vector<signed char>, because those three character types' sizeof is guaranteed to be 1.
There is no way to retrieve the amount of allocated memory from the pointer. Lets forget for a moment that standard containers (and smart pointers) exist, then you could use a struct that encapsulates the pointer and the size. The most simple dynamic array I can imagine is this:
template <typename T>
struct my_dynamic_array {
size_t capacity;
T* data;
my_dynamic_array(size_t capacity) : capacity(capacity),data(new T[capacity]) {}
~my_dynamic_array() { delete[] data; }
const T& operator[](int i) const { return data[i];}
T& operator[](int i) { return data[i];}
};
Note that his is just a basic example for the sake of demonstration, eg you shouldnt copy instances of this struct or bad things will happen. However, it can be used like this:
my_dynamic_array<int> x(5);
x[3] = 1;
std::cout << x[3];
ie no pointers and no manual memory allocation in the code using the array, which is a good thing. Actually, this alone is already a big deal, because now you can make use of RAII and cannot forget to delete the memory.
Next you may want to resize your array, which requires just a bit more boilerplate (again: take it with a grain of salt!):
template <typename T>
struct my_dynamically_sized_array : my_dynamic_array<T> {
size_t size;
my_dynamically_sized_array(size_t size, size_t capacity) :
my_dynamic_array<T>(capacity),size(size) {}
void push(const T& t) {
my_dynamic_array<T>::data[size] = t;
++size;
}
};
It can be used like this:
my_dynamically_sized_array<int> y(0,3);
y.push(3);
std::cout << y[0];
Of course the memory would need to be reallocated when the size grows bigger than the capacity and many more things would be required to make this wrapper really functional (eg being able to copy would be nice).
The bottom line is: Dont do any of this! To write a good full-blown container class much more than I can outline here is required and most of that is boiler-plate that doesnt really add value to your code base, because std::vector already is a thin wrapper around dynamically allocated memory that offers you all you need while not imposing overhead for stuff you dont use.

Allocation of variable-sized class

I have a variable length data structure, a multi-dimensional iterator:
class Iterator
{
public:
static Iterator& init(int dim, int* sizes, void* mem)
{
return *(new (mem) Iterator(dim, sizes));
}
static size_t alloc_size(int dim)
{
return sizeof(Iterator) + sizeof(int) * 2 * dim;
}
void operator++()
{
// increment counters, update pos_ and done_
}
bool done() const { return done_; }
bool pos() const { return pos_; }
private:
Iterator(int dim, int* sizes) : dim_(dim), pos_(0), done_(false)
{
for (int i=0; i<dim_; ++i) size(i) = sizes[i];
for (int i=0; i<dim_; ++i) counter(i) = 0;
}
int dim_;
int pos_;
bool done_;
int size (int i) { return reinterpret_cast<int*>(this+1)[i]; }
int& counter(int i) { return reinterpret_cast<int*>(this+1)[dim_+i]; }
};
The dimensionality of the iterator is not known at compile time but probably small, so I allocate memory for the iterator with alloca:
void* mem = alloca(Iterator::alloc_size(dim));
for (Iterator& i = Iterator::create(dim, sizes, mem); !i.done(); ++i)
{
// do something with i.pos()
}
Is there a more elegant way of allocating memory for the iterator? I am aware of the fact that upon returning from a function, its stack is unwound, thus alloca must be used in the caller's stack frame (see e.g. here). This answer suggests that the allocation be performed in a default parameter:
static Iterator& init(int dim, int* sizes, void* mem = alloca(alloc_size(dim)));
However elegant, this solution does not help me: Default argument references parameter 'dim'. Any suggestion for a nice solution?
Unfortunately, given that dim is a run-time value, there isn't any way to do this other than with a macro:
#define CREATE_ITERATOR(dim, sizes) \
Iterator::init(dim, sizes, alloca(Iterator::alloc_size(dim)))
You could have the dimension parameter as a template argument.
My suggestion might not be what you are looking for but why not have a create|make_iterator function that does the alloca call?
I wouldn't recommend to use alloca at all. If dim value is small, then some fixed-size buffer inside a class would be sufficient. If dim is large then heap allocation cost would be neglectable comparing to complexity of other operations performed on your iterator (note that for very large dim values alloca may cause stack overflow). You may choose between fixed buffer and heap allocation at runtime, depending on dim size.
So I'd recomend approach similar to small string optimization in std::string.
Perhaps, some kind of COW (copy on write http://en.wikipedia.org/wiki/Copy-on-write) techique may be also useful for your iterators.
Note, that this technique cannot be used with alloca, only with heap allocation. Morever, it's almost impossible to copy or copy initialize your iterators if they use alloca (at least without more and more ugly macroses).
Alloca is evil :)

STL vectors with uninitialized storage?

I'm writing an inner loop that needs to place structs in contiguous storage. I don't know how many of these structs there will be ahead of time. My problem is that STL's vector initializes its values to 0, so no matter what I do, I incur the cost of the initialization plus the cost of setting the struct's members to their values.
Is there any way to prevent the initialization, or is there an STL-like container out there with resizeable contiguous storage and uninitialized elements?
(I'm certain that this part of the code needs to be optimized, and I'm certain that the initialization is a significant cost.)
Also, see my comments below for a clarification about when the initialization occurs.
SOME CODE:
void GetsCalledALot(int* data1, int* data2, int count) {
int mvSize = memberVector.size()
memberVector.resize(mvSize + count); // causes 0-initialization
for (int i = 0; i < count; ++i) {
memberVector[mvSize + i].d1 = data1[i];
memberVector[mvSize + i].d2 = data2[i];
}
}
std::vector must initialize the values in the array somehow, which means some constructor (or copy-constructor) must be called. The behavior of vector (or any container class) is undefined if you were to access the uninitialized section of the array as if it were initialized.
The best way is to use reserve() and push_back(), so that the copy-constructor is used, avoiding default-construction.
Using your example code:
struct YourData {
int d1;
int d2;
YourData(int v1, int v2) : d1(v1), d2(v2) {}
};
std::vector<YourData> memberVector;
void GetsCalledALot(int* data1, int* data2, int count) {
int mvSize = memberVector.size();
// Does not initialize the extra elements
memberVector.reserve(mvSize + count);
// Note: consider using std::generate_n or std::copy instead of this loop.
for (int i = 0; i < count; ++i) {
// Copy construct using a temporary.
memberVector.push_back(YourData(data1[i], data2[i]));
}
}
The only problem with calling reserve() (or resize()) like this is that you may end up invoking the copy-constructor more often than you need to. If you can make a good prediction as to the final size of the array, it's better to reserve() the space once at the beginning. If you don't know the final size though, at least the number of copies will be minimal on average.
In the current version of C++, the inner loop is a bit inefficient as a temporary value is constructed on the stack, copy-constructed to the vectors memory, and finally the temporary is destroyed. However the next version of C++ has a feature called R-Value references (T&&) which will help.
The interface supplied by std::vector does not allow for another option, which is to use some factory-like class to construct values other than the default. Here is a rough example of what this pattern would look like implemented in C++:
template <typename T>
class my_vector_replacement {
// ...
template <typename F>
my_vector::push_back_using_factory(F factory) {
// ... check size of array, and resize if needed.
// Copy construct using placement new,
new(arrayData+end) T(factory())
end += sizeof(T);
}
char* arrayData;
size_t end; // Of initialized data in arrayData
};
// One of many possible implementations
struct MyFactory {
MyFactory(int* p1, int* p2) : d1(p1), d2(p2) {}
YourData operator()() const {
return YourData(*d1,*d2);
}
int* d1;
int* d2;
};
void GetsCalledALot(int* data1, int* data2, int count) {
// ... Still will need the same call to a reserve() type function.
// Note: consider using std::generate_n or std::copy instead of this loop.
for (int i = 0; i < count; ++i) {
// Copy construct using a factory
memberVector.push_back_using_factory(MyFactory(data1+i, data2+i));
}
}
Doing this does mean you have to create your own vector class. In this case it also complicates what should have been a simple example. But there may be times where using a factory function like this is better, for instance if the insert is conditional on some other value, and you would have to otherwise unconditionally construct some expensive temporary even if it wasn't actually needed.
In C++11 (and boost) you can use the array version of unique_ptr to allocate an uninitialized array. This isn't quite an stl container, but is still memory managed and C++-ish which will be good enough for many applications.
auto my_uninit_array = std::unique_ptr<mystruct[]>(new mystruct[count]);
C++0x adds a new member function template emplace_back to vector (which relies on variadic templates and perfect forwarding) that gets rid of any temporaries entirely:
memberVector.emplace_back(data1[i], data2[i]);
To clarify on reserve() responses: you need to use reserve() in conjunction with push_back(). This way, the default constructor is not called for each element, but rather the copy constructor. You still incur the penalty of setting up your struct on stack, and then copying it to the vector. On the other hand, it's possible that if you use
vect.push_back(MyStruct(fieldValue1, fieldValue2))
the compiler will construct the new instance directly in the memory thatbelongs to the vector. It depends on how smart the optimizer is. You need to check the generated code to find out.
You can use boost::noinit_adaptor to default initialize new elements (which is no initialization for built-in types):
std::vector<T, boost::noinit_adaptor<std::allocator<T>> memberVector;
As long as you don't pass an initializer into resize, it default initializes the new elements.
So here's the problem, resize is calling insert, which is doing a copy construction from a default constructed element for each of the newly added elements. To get this to 0 cost you need to write your own default constructor AND your own copy constructor as empty functions. Doing this to your copy constructor is a very bad idea because it will break std::vector's internal reallocation algorithms.
Summary: You're not going to be able to do this with std::vector.
You can use a wrapper type around your element type, with a default constructor that does nothing. E.g.:
template <typename T>
struct no_init
{
T value;
no_init() { static_assert(std::is_standard_layout<no_init<T>>::value && sizeof(T) == sizeof(no_init<T>), "T does not have standard layout"); }
no_init(T& v) { value = v; }
T& operator=(T& v) { value = v; return value; }
no_init(no_init<T>& n) { value = n.value; }
no_init(no_init<T>&& n) { value = std::move(n.value); }
T& operator=(no_init<T>& n) { value = n.value; return this; }
T& operator=(no_init<T>&& n) { value = std::move(n.value); return this; }
T* operator&() { return &value; } // So you can use &(vec[0]) etc.
};
To use:
std::vector<no_init<char>> vec;
vec.resize(2ul * 1024ul * 1024ul * 1024ul);
Err...
try the method:
std::vector<T>::reserve(x)
It will enable you to reserve enough memory for x items without initializing any (your vector is still empty). Thus, there won't be reallocation until to go over x.
The second point is that vector won't initialize the values to zero. Are you testing your code in debug ?
After verification on g++, the following code:
#include <iostream>
#include <vector>
struct MyStruct
{
int m_iValue00 ;
int m_iValue01 ;
} ;
int main()
{
MyStruct aaa, bbb, ccc ;
std::vector<MyStruct> aMyStruct ;
aMyStruct.push_back(aaa) ;
aMyStruct.push_back(bbb) ;
aMyStruct.push_back(ccc) ;
aMyStruct.resize(6) ; // [EDIT] double the size
for(std::vector<MyStruct>::size_type i = 0, iMax = aMyStruct.size(); i < iMax; ++i)
{
std::cout << "[" << i << "] : " << aMyStruct[i].m_iValue00 << ", " << aMyStruct[0].m_iValue01 << "\n" ;
}
return 0 ;
}
gives the following results:
[0] : 134515780, -16121856
[1] : 134554052, -16121856
[2] : 134544501, -16121856
[3] : 0, -16121856
[4] : 0, -16121856
[5] : 0, -16121856
The initialization you saw was probably an artifact.
[EDIT] After the comment on resize, I modified the code to add the resize line. The resize effectively calls the default constructor of the object inside the vector, but if the default constructor does nothing, then nothing is initialized... I still believe it was an artifact (I managed the first time to have the whole vector zerooed with the following code:
aMyStruct.push_back(MyStruct()) ;
aMyStruct.push_back(MyStruct()) ;
aMyStruct.push_back(MyStruct()) ;
So...
:-/
[EDIT 2] Like already offered by Arkadiy, the solution is to use an inline constructor taking the desired parameters. Something like
struct MyStruct
{
MyStruct(int p_d1, int p_d2) : d1(p_d1), d2(p_d2) {}
int d1, d2 ;
} ;
This will probably get inlined in your code.
But you should anyway study your code with a profiler to be sure this piece of code is the bottleneck of your application.
I tested a few of the approaches suggested here.
I allocated a huge set of data (200GB) in one container/pointer:
Compiler/OS:
g++ (Ubuntu 9.4.0-1ubuntu1~20.04.1) 9.4.0
Settings: (c++-17, -O3 optimizations)
g++ --std=c++17 -O3
I timed the total program runtime with linux-time
1.) std::vector:
#include <vector>
int main(){
constexpr size_t size = 1024lu*1024lu*1024lu*25lu;//25B elements = 200GB
std::vector<size_t> vec(size);
}
real 0m36.246s
user 0m4.549s
sys 0m31.604s
That is 36 seconds.
2.) std::vector with boost::noinit_adaptor
#include <vector>
#include <boost/core/noinit_adaptor.hpp>
int main(){
constexpr size_t size = 1024lu*1024lu*1024lu*25lu;//25B elements = 200GB
std::vector<size_t,boost::noinit_adaptor<std::allocator<size_t>>> vec(size);
}
real 0m0.002s
user 0m0.001s
sys 0m0.000s
So this solves the problem. Just allocating without initializing costs basically nothing (at least for large arrays).
3.) std::unique_ptr<T[]>:
#include <memory>
int main(){
constexpr size_t size = 1024lu*1024lu*1024lu*25lu;//25B elements = 200GB
auto data = std::unique_ptr<size_t[]>(new size_t[size]);
}
real 0m0.002s
user 0m0.002s
sys 0m0.000s
So basically the same performance as 2.), but does not require boost.
I also tested simple new/delete and malloc/free with the same performance as 2.) and 3.).
So the default-construction can have a huge performance penalty if you deal with large data sets.
In practice you want to actually initialize the allocated data afterwards.
However, some of the performance penalty still remains, especially if the later initialization is performed in parallel.
E.g., I initialize a huge vector with a set of (pseudo)random numbers:
(now I use fopenmp for parallelization on a 24 core AMD Threadripper 3960X)
g++ --std=c++17-fopenmp -O3
1.) std::vector:
#include <vector>
#include <random>
int main(){
constexpr size_t size = 1024lu*1024lu*1024lu*25lu;//25B elements = 200GB
std::vector<size_t> vec(size);
#pragma omp parallel
{
std::minstd_rand0 gen(42);
#pragma omp for schedule(static)
for (size_t i = 0; i < size; ++i) vec[i] = gen();
}
}
real 0m41.958s
user 4m37.495s
sys 0m31.348s
That is 42s, only 6s more than the default initialization.
The problem is, that the initialization of std::vector is sequential.
2.) std::vector with boost::noinit_adaptor:
#include <vector>
#include <random>
#include <boost/core/noinit_adaptor.hpp>
int main(){
constexpr size_t size = 1024lu*1024lu*1024lu*25lu;//25B elements = 200GB
std::vector<size_t,boost::noinit_adaptor<std::allocator<size_t>>> vec(size);
#pragma omp parallel
{
std::minstd_rand0 gen(42);
#pragma omp for schedule(static)
for (size_t i = 0; i < size; ++i) vec[i] = gen();
}
}
real 0m10.508s
user 1m37.665s
sys 3m14.951s
So even with the random-initialization, the code is 4 times faster because we can skip the sequential initialization of std::vector.
So if you deal with huge data sets and plan to initialize them afterwards in parallel, you should avoid using the default std::vector.
From your comments to other posters, it looks like you're left with malloc() and friends. Vector won't let you have unconstructed elements.
From your code, it looks like you have a vector of structs each of which comprises 2 ints. Could you instead use 2 vectors of ints? Then
copy(data1, data1 + count, back_inserter(v1));
copy(data2, data2 + count, back_inserter(v2));
Now you don't pay for copying a struct each time.
If you really insist on having the elements uninitialized and sacrifice some methods like front(), back(), push_back(), use boost vector from numeric . It allows you even not to preserve existing elements when calling resize()...
I'm not sure about all those answers that says it is impossible or tell us about undefined behavior.
Sometime, you need to use an std::vector. But sometime, you know the final size of it. And you also know that your elements will be constructed later.
Example : When you serialize the vector contents into a binary file, then read it back later.
Unreal Engine has its TArray::setNumUninitialized, why not std::vector ?
To answer the initial question
"Is there any way to prevent the initialization, or is there an STL-like container out there with resizeable contiguous storage and uninitialized elements?"
yes and no.
No, because STL doesn't expose a way to do so.
Yes because we're coding in C++, and C++ allows to do a lot of thing. If you're ready to be a bad guy (and if you really know what you are doing). You can hijack the vector.
Here a sample code that works only for the Windows's STL implementation, for another platform, look how std::vector is implemented to use its internal members :
// This macro is to be defined before including VectorHijacker.h. Then you will be able to reuse the VectorHijacker.h with different objects.
#define HIJACKED_TYPE SomeStruct
// VectorHijacker.h
#ifndef VECTOR_HIJACKER_STRUCT
#define VECTOR_HIJACKER_STRUCT
struct VectorHijacker
{
std::size_t _newSize;
};
#endif
template<>
template<>
inline decltype(auto) std::vector<HIJACKED_TYPE, std::allocator<HIJACKED_TYPE>>::emplace_back<const VectorHijacker &>(const VectorHijacker &hijacker)
{
// We're modifying directly the size of the vector without passing by the extra initialization. This is the part that relies on how the STL was implemented.
_Mypair._Myval2._Mylast = _Mypair._Myval2._Myfirst + hijacker._newSize;
}
inline void setNumUninitialized_hijack(std::vector<HIJACKED_TYPE> &hijackedVector, const VectorHijacker &hijacker)
{
hijackedVector.reserve(hijacker._newSize);
hijackedVector.emplace_back<const VectorHijacker &>(hijacker);
}
But beware, this is hijacking we're speaking about. This is really dirty code, and this is only to be used if you really know what you are doing. Besides, it is not portable and relies heavily on how the STL implementation was done.
I won't advise you to use it because everyone here (me included) is a good person. But I wanted to let you know that it is possible contrary to all previous answers that stated it wasn't.
Use the std::vector::reserve() method. It won't resize the vector, but it will allocate the space.
Do the structs themselves need to be in contiguous memory, or can you get away with having a vector of struct*?
Vectors make a copy of whatever you add to them, so using vectors of pointers rather than objects is one way to improve performance.
I don't think STL is your answer. You're going to need to roll your own sort of solution using realloc(). You'll have to store a pointer and either the size, or number of elements, and use that to find where to start adding elements after a realloc().
int *memberArray;
int arrayCount;
void GetsCalledALot(int* data1, int* data2, int count) {
memberArray = realloc(memberArray, sizeof(int) * (arrayCount + count);
for (int i = 0; i < count; ++i) {
memberArray[arrayCount + i].d1 = data1[i];
memberArray[arrayCount + i].d2 = data2[i];
}
arrayCount += count;
}
I would do something like:
void GetsCalledALot(int* data1, int* data2, int count)
{
const size_t mvSize = memberVector.size();
memberVector.reserve(mvSize + count);
for (int i = 0; i < count; ++i) {
memberVector.push_back(MyType(data1[i], data2[i]));
}
}
You need to define a ctor for the type that is stored in the memberVector, but that's a small cost as it will give you the best of both worlds; no unnecessary initialization is done and no reallocation will occur during the loop.