I have a const float* pointing to a huge array, and would like to be able to access the elements through a std::array. What is the best way to do this? Without copying the elements if possible.
Thanks!
In order to use std::array, you need to know the size of the array at compile time. You create an empty array and use std::copy to copy the elements into the array.
If the code which uses your const float* only knows that size at runtime, then you cannot use std::array but have to use std::vector. std::vector has a constructor to which you can pass pointers to the begin and end of the range to copy into it.
Note that in both cases, the container owns a copy of the original elements.
Without copying the elements if possible.
No, that is not possible. C++ standard containers are designed to own their contents, not just representing a view into them.
Here is an example to illustrate the difference:
#define SIZE 10 // let's assume some C or legacy code which uses macros
// ...
void f(const float* arr)
{
// size is known at compile time
std::array<float, SIZE> a;
std::copy(arr, arr + SIZE, begin(a));
}
void g(const float* arr, int size)
{
// size is only known at runtime
std::vector<float> v(arr, arr + size);
}
There are at least two proposed additions to the standard library that do something like that. One is std::experimental::to_array<T,N>(), which makes a deep copy of the entire data. That’s wasteful in terms of both time and memory, but could be useful if you really do want to create a copy, especially a const copy which you cannot create and then modify.
If what you want is a container interface to a range of data represented by an arbitrary pointer and element count, the Guideline Support Library offers a span template. I recommend that, if you pass both, you wrap them in a lightweight object similar to this.
Since you said you can change the interface to the constructor, I strongly suggest you do so. If you want to keep around the version that takes a std::array, you can, and delegate it to the version that takes an arbitrary range of data, such as:
#include <algorithm>
#include <array>
#include <iterator>
#include "span"
MyClass::MyClass( const span<value_type>& s /*, more, params */ )
{
/* In this example, m_storage is some private container and span has a
* container interface.
*/
auto it = std::back_inserter(m_storage);
std::copy_n( s.begin(), s.size(), it );
// ...
}
MyClass::MyClass( std::array<value_type, initializer_size>& a /*, more, params */ )
: MyClass( span<value_type>( a.data(), a.size() ) /*, more, params */ )
{}
array_view.std::array does not take ownership of pointers. You have to copy the elements. Use std::copy. It works on abstract iterators, for which pointers also qualify.
const float *source = ...; // C array of at least of size N
std::array<float, N> destination;
std::copy(source, source + N, destination.begin());
An aside: without copying the elements, the element type of whatever container with support for taking ownership of a pointer would have to be const float as well. Or you would have to use const_cast<> in case you were sure that the pointer is actually not really to const float. Please, only use const_cast<> in cases where it's absolutely unavoidable due to a flaw in an external API though :)
std::array. Can you talk to the developer to improve the API, e. g. accept a pair of iterators instead?