Jake
Jake

Reputation: 7773

Variable-sized objects on the stack

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?

Upvotes: 2

Views: 1276

Answers (3)

Tony Delroy
Tony Delroy

Reputation: 106066

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.

Upvotes: 3

Jack
Jack

Reputation: 133567

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();

Upvotes: 4

Sam Varshavchik
Sam Varshavchik

Reputation: 118292

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.

Upvotes: 3

Related Questions