What Is the Use of 0-Length Array (Or Std::Array)

What is the use of 0-length array (or std::array)?

Your first example is not standard C++ but is an extension that both gcc and clang allow, it is version of flexible arrays and this answer to the question: Are flexible array members really necessary? explains the many advantages of this feature. If you compiled using the -pedantic flag you would have received the following warning in gcc:

warning: ISO C++ forbids zero-size array 'arr1' [-Wpedantic]

and the following warning in clang:

warning: zero size arrays are an extension [-Wzero-length-array]

As for your second case zero-length std::array allows for simpler generic algorithms without having to special case for zero-length, for example a template non-type parameter of type size_t. As the cppreference section for std::array notes this is a special case:

There is a special case for a zero-length array (N == 0). In that case, array.begin() == array.end(), which is some unique value. The effect of calling front() or back() on a zero-sized array is undefined.

It would also make it consistent with other sequence containers which can also be empty.

What happens if I define a 0-size array in C/C++?

An array cannot have zero size.

ISO 9899:2011 6.7.6.2:

If the expression is a constant expression, it shall have a value greater than zero.

The above text is true both for a plain array (paragraph 1). For a VLA (variable length array), the behavior is undefined if the expression's value is less than or equal to zero (paragraph 5). This is normative text in the C standard. A compiler is not allowed to implement it differently.

gcc -std=c99 -pedantic gives a warning for the non-VLA case.

Is there a reason for zero sized std::array in C++11?

If you have a generic function it is bad if that function randomly breaks for special parameters. For example, lets say you could have a template function that takes N random elements form a vector:

template<typename T, size_t N>
std::array<T, N> choose(const std::vector<T> &v) {
...
}

Nothing is gained if this causes undefined behavior or compiler errors if N for some reason turns out to be zero.

For raw arrays a reason behind the restriction is that you don't want types with sizeof T == 0, this leads to strange effects in combination with pointer arithmetic. An array with zero elements would have size zero, if you don't add any special rules for it.

But std::array<> is a class, and classes always have size > 0. So you don't run into those problems with std::array<>, and a consistent interface without an arbitrary restriction of the template parameter is preferable.

std::array of size zero

What does it mean to have std::array,array of size zero?

The same as for example an empty std::vector or an empty std::set.

Why isn't it defined as illegal?

It is desirable to make it legal because it means generic programming does not have to handle a special case when the std::array's size is the result of a compile-time calculation.

It is possible to define it as legal thanks to template specialisation. For example, the implementation that comes with Visual C++ specialises std::array in a fashion similar to the following:

template<class T>
class array<T, 0> // specialisation
{
// ...

size_type size() const
{
return 0;
}

T elements[1]; // the raw array cannot have a size of 0
};

I suppose every compiler implements std::array like that.

std::decay an zero length array

We could let the compiler's type checker, instead of template substitution, to do the decay for us:

#include <type_traits>

template <typename T>
T* as_ptr(T* x) { return x; }

template <typename T>
using DecayToPointer = decltype(as_ptr(std::declval<T>()));

int main() {
static_assert(std::is_same<DecayToPointer<int[0]>, int*>::value, "");
static_assert(std::is_same<DecayToPointer<int[1]>, int*>::value, "");
static_assert(std::is_same<DecayToPointer<int[]>, int*>::value, "");
}

What is the zero-length array mentioned in the draft standard?

As far as I understand this is to allow new to be implemented in terms of malloc which allows zero sized requests. You can not do much with them since as noted in the quote below dereferencing such a pointer is undefined behavior.

We can find a rationale in the draft C++ standard footnote 35 which is referenced from section 3.7.4.1 [basic.stc.dynamic.allocation]:

[...]Even if the size of the space requested is zero, the request can fail.
If the request succeeds, the value returned shall be a non-null pointer value (4.10) p0 different from any
previously returned value p1, unless that value p1 was subsequently passed to an operator delete. The
effect of dereferencing a pointer returned as a request for zero size is undefined.35

and footnote 35 says:

The intent is to have operator new() implementable by calling std::malloc() or std::calloc(), so the rules are substantially
the same. C++ differs from C in requiring a zero request to return a non-null pointer.

From the C11 draft standard section 7.22.3 Memory management functions:

[...]If the size of
the space requested is zero, the behavior is implementation-defined: either a null pointer
is returned, or the behavior is as if the size were some nonzero value, except that the
returned pointer shall not be used to access an object.

How to pass an array of zero size to template function

Arrays of zero size are definitely not standard C++. From [dcl.array]:

In a declaration T D where D has the form

D1 [ constant-expressionopt ] attribute-specifier-seqopt

[...]

If the constant-expression is present, it shall be a converted constant expression of type std​::​size_­t and its value shall be greater than zero.

GCC however provides arrays of zero length as an extension but since they are non-standard you can't expect them to work together with other language features, such as template argument deduction. You could make an explicit overload of the function for zero length arrays.

#include <cstdlib>

template<typename T, std::size_t N>
void someFun(T (&)[N]) {}

template<typename T>
void someFun(T (&)[0]) {}

int main() {
int arr[0];
someFun(arr);
}

This compiles on GCC (7.2.0) but not on Clang (6.0.0).



Related Topics



Leave a reply



Submit