2

I can create a variable-sized C-style array on the stack with no problems:

void f(int n) {
  float data1[n]; // OK
}

But I can't do the same with std::array:

void f(int n) {
  std::array<float,n> data2; // error: ‘n’ is not a constant expression
}

The following example illustrates my point more thoroughly:

#include <array>
#include <cstdio>

template<int N>
class A{
  public:
    void print() { 
      printf("data[0]: %2.2f\n", data[0]);
    }
  private:
    float data[N];
};

void f(int n) {
  float data1[n]; // OK
  std::array<float,n> data2; // error: ‘n’ is not a constant expression
  A<n> data3; // error: ‘n’ is not a constant expression
}

int main(int argc, char **argv) {
  f(3);
}

My Questions: Is there a way to create a variable-sized class/struct strictly on the stack? If so, how? If not, is this due simply to the specifications or is there an underlying technical reason that it is forbidden?

Jake
  • 7,565
  • 6
  • 55
  • 68

3 Answers3

4

You are comparing apples with oranges.

When you use a variable-length array you refer to a feature that comes from older days (introduced in C99) that allows specifically to allocate arrays on the stack without knowing the size at compile time. This is done by the compiler by just reserving the required stack size at runtime instead that precompute the displacement before.

If I remember correctly it has actually been relegated in C++11 to a status of optional feature (while C11 brought it along).

Then you have std::array<T,size_t> which is something completely different. It's a template and it is required to be instantiated with compile time types / values. So you simply can't do what you want.

The main point is that C++ is a language which allows you to avoid the need of VLA, since you have std::vector and dynamic allocation.

If you really want to allocate things on stack you must look for specific and dirty tricks which aren't needed 99.9% of the time like placement new and alloca (which is non standard in any case), eg:

void *reserved = alloca(sizeof(MyClass));
new (reserved) MyClass();
Jack
  • 131,802
  • 30
  • 241
  • 343
  • 1
    Bjarne Stroustroup, the creator of C++, also makes a point against VLAs in his talks and [writings](http://www.stroustrup.com/examples_short.pdf). – Felix Dombek Apr 12 '16 at 02:56
  • "The main point is that C++ is a language which allows you to avoid the need of VLA, since you have std::vector and dynamic allocation." - C also has dynamic allocation, so you also don't need VLAs in C. They're for efficiency. – user253751 Apr 12 '16 at 04:03
  • @immibis: Indeed they're totally optional also in C, I checked the standard to be sure of my memory and already in C11 they are considered optional, from §6.7.6.2: *"Variable length arrays are a conditional feature that implementations need not support; see 6.10.8.3.)"*. If you want efficiency just don't use C++, who wants to pay for constructors when they are not needed. Don't use `std::function`, never use `std::vector` and just use plain C arrays. Efficiency is not everything and with today CPUs is surely less important to be able to allocate arrays on stack. – Jack Apr 12 '16 at 04:10
  • @Jack It wasn't about whether compilers are required to support them, it was about whether programmers are required to use them. – user253751 Apr 12 '16 at 04:27
  • @immibis: yes, what I mean is that the standard is not a mere exercise of formalization. It's designed to give programmers what it is thought to be useful. And as I already said in my answer 99.99% of the time you don't need VLA. And for the 0.01% of when you *think* you might need it, first you need to prove that using a VLA yields a non-negligible performance improvement. On my machine a simple test case (http://ideone.com/zApSBK) compiled with `-Os` gives better results with heap allocated arrays. Go figure out. – Jack Apr 12 '16 at 13:07
3

std::array is not a "a variable-sized class/struct". It is a template. It happens to implement an array container, of the length specified by the second template parameter, but this is not the same as "a variable-sized class/struct".

std::array template's second parameter must be a constant value. A template parameter can be either a class, or a constant value. It cannot be a non-constant value. That's how templates work.

What you probably want is a std::vector. Declare it, and resize() it. That's your variably-sized array.

Sam Varshavchik
  • 114,536
  • 5
  • 94
  • 148
  • 1
    Maybe it should also be mentioned that variable length array is a feature of C11 and is not allowed in C++. – Lingxi Apr 12 '16 at 02:45
  • I am aware that `std::array` is not variable-sized; I was using it as an example of an array-like object that can exist solely on the stack. As I indicated in the question at the bottom, I'm specifically interested in stack-allocated memory. `std::vector` stores data on the heap. – Jake Apr 12 '16 at 07:15
3

Is there a way to create a variable-sized class/struct strictly on the stack?

No. There is no support for variable-sized class/struct in the C++ Standard, so they can't be created anywhere. That said, you can create a class/struct in which the final data member is an array, and deliberately overrun the array dimensions. This trick's known as the "struct hack" in C - see the FAQ. Whether it's undefined behaviour depends on your implementation. You can use it in combination with alloca and placement-new to create such a variable on the stack, but should be careful to manually delete it afterwards if your program depends on any side-effects of the destructor.

If not, is this due simply to the specifications or is there an underlying technical reason that it is forbidden?

The reason is performance. Normally, variables on the stack can be accessed using the stack pointer CPU register plus a constant offset, which the compiler can calculate for any given local/automatic variable at compiler time. When you have variable length content on the stack, the local variables after that are no longer at fixed offsets from the stack pointer register, which means the compiler has to generate code to add various values in order to calculate a runtime offset at which to access the variables. When you have several variable-length stack-hosted variables, the problems compound and the performance degrades further. To avoid programmers producing unexpectedly slow code, and to keep compilers simpler, the Standard doesn't require support for variable length types.

Tony Delroy
  • 102,968
  • 15
  • 177
  • 252
  • That sounds like an excellent reason to avoid variable-length arrays altogether. Thanks for your input! – Jake Apr 12 '16 at 07:19