简体   繁体   中英

Create std::vector in-place from raw data

Given a raw array of elements, how to create a std::vector that takes ownership of the raw array without reallocate & copy?

For example having the raw array:

int* elems = new int[33]

how to create a std::vector of size 33 pointing to elems ?

I am sure that theoretically this is possible, as usually std::vector is implemented as a structure containing three pointers, one pointing to the beginning of the allocated memory, one to the end of the valid elements, and one to the end of the allocated memory. But is there a standard way of initializing the std::vector structure with the raw array?

What you need is a "view" rather than a container. Containers own their elements and their main purpose is to encapsulate the raw memory they manage. If you need to manage the memory yourself then you dont need a container. Take a look at string_view that would be your solution if you had a string . Perhaps boost ranges is something that you could apply. From the docs (emphasize mine):

The motivation for the Range concept is that there are many useful Container-like types that do not meet the full requirements of Container, and many algorithms that can be written with this reduced set of requirements. In particular, a Range does not necessarily

  • own the elements that can be accessed through it ,
  • have copy semantics,

PS: Actually std::array_view was considered for C++17, but unfortunately it didnt make it into the standard.

The reason why this is not directly possible is that the standard library uses allocators to reserve memory for the containers.

Therefore if you have an std::vector which uses a certain type of allocator and give it some pointer you have created, you effectively break the allocator idiom. If your implementation of the standard library for example uses malloc and free instead of new and delete , your program will fail.

For this to become a standard way , the standard library would need to provide a constructor that accepts a T* which additionally must have been returned by the same allocator the vector uses later. So the signature of the constructor you need would be something like std::vector::vector(T* data, size_type used_elements, size_type capacity, const Allocator& alloc) . Notice that the allocator argument is necessary as the T* must (theoretically) have been returned by the exact same allocator that is used in the vector.


You can achieve some of the functionality this by creating your own allocator according to this concept , but to have your 33 elements to not be reconstructed you will also have to provide a allocator::construct(..) function which is a no-op until the 34th element (exclusive). Additionally you will have to initially resize your vector to 33 elements to force the vector to have the correct size.

That being said this nevertheless is a bad idea because for the conditional construct and allocate functions you will probably have more overhead opposed to copying your elements once.

In accordance to this , there is no constructor that accepts pointer to data. So, you can't pass the ownership over raw array to vector.

You can only create a vector and place data into it.

Given a raw array of elements, how to create a std::vector that takes ownership of the raw array without reallocate & copy?

There is no way.

how to create a std::vector of size 33 pointing to elems?

Impossible.

I am sure that theoretically this is possible,

No, it isn't.

But is there a standard way of initializing the std::vector structure with the raw array?

No.


That being said, chances are that you may be able to hack together a solution with a custom allocator. However, in addition to the fact that writing custom allocators is a rarely-used error-prone technique, you shouldn't overestimate the usability of such a solution.

std::vector<int> and std::vector<int, MyAllocator> are two different classes . If your goal is to interface with code that expects std::vector<int> , then std::vector<int, MyAllocator> cannot be used; and if you intend to create and use std::vector<int, MyAllocator> in your code, then honestly you'd be better off just implementing your own non-owning container class, ie something like a custom VectorView<T> .

If the type of the object you are working on is movable, you can do this:

template<typename T>
std::vector<std::unique_ptr<T>> ConvertArrayToVector(T* data, size_t size)
{
    std::vector<std::unique_ptr<T>> result(size);
    for (unsigned int i = 0; i<size; ++i)
        result[i] = std::make_unique<T>(std::forward<T>(data[i]));

    return result;
}

The resulting vector owns the array now, in a sense that it stores pointers to its elements and makes sure the objects are deleted when the vector is destroyed, but the original array gets invalidated in the process.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM