C++ struct array member without given dimension (flexible array member?)

2.4k Views Asked by At

I compiled and ran the following C++ code, blindly trying to create a flexible array member like you can in C:

#include <iostream>

template <typename T>
struct Vector {
    int length;
    T ts[];
};

Vector<int> ts = {
    3,
    {10, 10, 10},
};


int main() {
    std::cout << sizeof(ts) << std::endl;
    std::cout << ts.data[1] << std::endl;
    return 0;
}

The code compiles and runs just fine, and gives the same output that C would in the same circumstance (it outputs 4 and then 10).

Now, according to this answer from 2010 what I have written should not be valid C++. Furthermore, according to this wikipedia article, "C++ does not have flexible array members".

My question is, which C++ feature am I actually using in the above code, specifically on the line that says "T ts[];"? Does that code actually do what I think it does in general, or is it undefined behavior?

1

There are 1 best solutions below

0
On BEST ANSWER

It is one of those things which are different between C and C++. A flexible array member is valid in C but not C++.

That said, many modern compilers compile C as a subset of C++, taking care to care only when you torque up the compiler error diagnostics.

David Tribble spends a moment on it at his Incompatibilities Between ISO C and ISO C++ page, where he specifically addresses this issue:

C++ does not support flexible array members.

(This feature might be provided as an extension by some C++ compilers, but would probably be valid only for POD structure types.)

So yes, this is undefined behavior. The correct way (in both C and C++) to write such a thing is to give it a non-zero dimension:

template <typename T>
struct Vector {
    int length;
    T ts[1];
};

You have another issue: you must allocate memory for said object. Simply specifying an initializer is not enough. As far as every access to such a thing exists, the compiler only ever thinks it is its minimal size.

This “range hack” is so called because the programmer explicitly uses/abuses C's (and C++'s) ability to violate range bounds to do something tricky.

The consequences of this are many, including inability to store these things in any standard container, or pass them around by value, or do most anything that eschews handling it through a pointer. It has its place, but for the vast majority of use cases C++ has superior options.