4

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!

5
  • 3
    This is not possible without undefined behavior. Why do you need it? Commented Mar 26, 2017 at 12:18
  • I need to construct an object, and one of the arguments in its constructor is a std::array. And the values are stored in eigen's RealVectorType(eigen.tuxfamily.org/dox/…) This type has a data() method, which returns a float*. Commented Mar 26, 2017 at 12:40
  • 1
    If the class constructor takes an std::array then the size must be known at compile time -- right? Can you show the code for the class you're trying to construct? Commented Mar 26, 2017 at 12:46
  • 2
    This object you need to construct has a badly designed API if it forces you to use std::array. Can you talk to the developer to improve the API, e. g. accept a pair of iterators instead? Commented Mar 26, 2017 at 12:48
  • Yes, I know the size at compile time. This is the class: pastebin.com/FU2vxWBS I made it, so it is fine to change it. Commented Mar 26, 2017 at 13:07

3 Answers 3

3

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);
}
Sign up to request clarification or add additional context in comments.

4 Comments

Macros are not a legacy feature.
@yeoman: Using a macro to define a container size is largely a legacy feature in modern C++. Besides, the comment does not say at all that every use of a macro means legacy code.
That's true :) But being able to use macros for logging, assertion-like functionality, error handling in exception-free contexts, &c. is actually a strong language feature of C++. Yes, C macros are DUMB. Real macros like LISP and RUST have would be WAY preferrable. But having macros at all, albeit in a form that requires you to really think about how the result is going to be parsed when you write them, helps avoid a lot of boilerplate, which is in most cases a good idea :)
Yes, I actually know the size.
2

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 */ )
{}

1 Comment

In addition, Microsoft has a class template named array_view.
0

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 :)

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.