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.