1

So std::array and boost::array (which are almost identical, and I will hereafter refer to ambiguously as just "array") were designed to provide a container object for arrays that does not incur the overheads of vector that are unnecessary if the array does not dynamically change size. However, they are both designed by taking the array size not as a constructor parameter but a template argument. The result: vector allows dynamic resizing after object creation; array requires the size to be known at compile time.

As far as I can see, if you have an array for which you will knows the size at object creation but not at compile time, then your only options are 1) unnecessarily incur extra overheads by using vector, 2) use the (non-container type) native array (e.g., int foo[42];), or 3) write your own array-wrapper class from scratch. So is this correct, that this is an in-between case where you may want to use array rather than vector, but cannot? Or is there some magic I can do that will may array work for me?

Here's a little detail (ok, a lot) on what inspired this question, in case it helps you understand:

I have a module - say the caller - that will repeatedly produce binary data at runtime (unsigned char[], or array), and then pass it to another module - say the callee. The callee module does not modify the array (it will make a copy and modify that if necessary), so once the caller module creates the array initially, it will not change size (nor indeed contents). However, two problems arise: 1) The caller may not generate arrays of the same size each time an array is generated - it will know the array size at rutime when it creates the array, but not at compile time. 2) The method for caller to pass the array to callee needs to be able to take an array of whatever size the caller passes to it.

I thought about making it a templated function, e.g.,

template<size_t N> void foo(const array<unsigned char, N>& my_array);

However, I'm using an interface class to separate interface from implementation in the callee module. Therefore, the function must be a virtual method, which is mutually exclusive with being templated. Furthermore, even if that were not an issue, it would still have the same problem as #1 above - if the array sizes are not known at compile time then it also cannot resolved the templated function at compile time.

My actual funciton:

virtual void foo(const array<unsigned char, N>& my_array); // but what is N???

So in summary, am I correct that my only real choices are to use a vector or native array, e.g.,

virtual void foo(const vector<unsigned char> my_array); // unnecessary overhead
virtual void foo(const unsigned char[] my_array, size_t my_array_len); // yuk

Or is there some trick I'm overlooking that will let me use a std::array or boost::array?

Dave Lillethun
  • 2,978
  • 3
  • 20
  • 24
  • 2
    C++14 has `std::dynarray` and runtime-sized arrays. – chris Jul 09 '13 at 23:32
  • Have you determined how much overhead use of `std::vector` requires, and identified it as a problem? I ask because it is really very little overhead (if you use it right, 1 extra pointer per container): `std::dynarray`s big advantage is really about the possibility of stack based storage. – Yakk - Adam Nevraumont Jul 10 '13 at 01:56
  • Yeah, the truth is the overhead of std::vector probably won't kill me... That's what I'm falling back on for now. – Dave Lillethun Jul 10 '13 at 20:51

3 Answers3

4

Until we have std::dynarray in C++11, you can use std::unique_ptr:

std::unique_ptr<Foo[]> arr(new Foo[100]);

You can use this as arr[0], arr[1], etc., and it will call the correct delete[] upon destruction. The overhead is minimal (just the pointer).

I think the only difference between an array-typed unique pointer and std::dynarray is that the latter has iterators and and size other "containery" properties, and that it'll be in the "Containers" section rather than the "general utilities". [Update: And that compilers may choose to natively support dynarray and optimize it to use stack storage.]

Kerrek SB
  • 464,522
  • 92
  • 875
  • 1,084
  • Motivation behind std::dynarray is that it can optionally allocate its memory from stack. This is a replacement to C99s variable length arrays. dynarray is probable C++14 – RareBox Jul 10 '13 at 00:05
  • @RareBox: Separately, C++14 will *also* have variable-length arrays. (But you're right that N2648 suggests stack allocation for `std::dynarray` as QoI.) – Kerrek SB Jul 10 '13 at 00:07
1

You simply cannot use any form of std::array if you don't know the length at compile time.

If you don't know the size of your array at compile time, seriously consider using std::vector. Using variable length arrays (like int foo[n]), is not standard C++ and will cause stack overflows if given length is big enough. Also you cannot write any array-like-wrapper with (measurably) less overhead than std::vector.

I would just use

virtual void foo(const unsigned char* my_array, size_t my_array_len);

And call it like

obj.foo(&vec[0], vec.size());

There is no overhead attached and it does what you want. In addition to normal arrays (int foo[42]) this can also be called with vectors and std::arrays with zero overhead.

RareBox
  • 141
  • 4
  • /Well you could write one with less overhead than `std::vector` by a tiny, yet measurable amount. It would take a bunch of effort to get it to be as solid as `std::vector`, and you would save one pointer per container. Unless you have 1000000s of `vector`s each with tiny amounts of data (0 to3 elements of pointer size or less), or are developing on an emebedded system, it would be challenging to black box notice the difference. – Yakk - Adam Nevraumont Jul 10 '13 at 02:00
  • Thanks for the answer, @RareBox. I was trying to avoid that because my_array and my_array_len are not part of the same object, making two things that the caller needs to keep track of. std::vector and std::array track the size for you, so as the callee I can use a size() method and know for sure I'm getting the right answer. So I decided for now to just use vector and incur the overhead, and only if and when I see a performance problem (doubtful in this case, to be honest) then I'll use the char* and size parameter method. – Dave Lillethun Jul 11 '13 at 04:59
0

Other considerations:

  • Arrays are allocated on the stack. This is much faster than allocating on the heap.
  • Arrays always initialize all their elements when they are created.

So:

    class Foo;
    std::array<Foo, 100> aFoo;

constructs 100 Foo objects, (calls Foo::Foo() 100 times) while

    std::vector<Foo> vFoo;
    vFoo.reserve(100);

reserves space for 100 Foo objects (on the heap), but doesn't construct any of them.

Marshall Clow
  • 15,972
  • 2
  • 29
  • 45