struct A {
~A () {}
};
struct S {
S() : i(0) { }
~S() {}
int i;
// This fails:
//
// A a[];
A a[0];
};
int main()
{
struct A aaa;
return 0;
}
Note that if the zero-sized array is replaced by Flexible Array Member (FAM) A a[];
, an error will occur: error: unknown array size in delete
.
I found this example from here.
I encountered this issue in my custom dynamically expanding container class.
My questions: how to solve it? Is a zero-sized array a valid replacement and works well?
For example, to use operator new
to allocate memory for the instance of the class and use operator delete
to release it like this:
size_t totalSize = sizeof(MyContainer) + capacity * sizeof(MyElement);
void* memory = operator new(totalSize);
MyContainer* container = new (memory) MyContainer();
// ...
operator delete((void*)container);
Flexible array members do not exist in standard C++. They may (or may not) be supported as a language extension by your specific compiler. If so, their use with other C++ features will probably be severely limited.
Replacing a flexible array member with A a[0];
does not make it better. An array can not be declared to have zero size in standard C++ either. Again, this will be some language extension of your specific compiler if it compiles. And, in contrast to proper flexible array members, they are not even standard C either, making it even less clear what the semantics are.
delete
can't possibly work with a flexible array member of a type with non-trivial destructor. delete
needs to know the size of the array because it would need to call the non-trivial destructor of each element of the array. But it has no way of knowing that size.
You need to allocate memory with operator new
/operator new[]
yourself, create objects in the memory so obtained with placement-new or std::construct_at
and manually destroy each element you created with an explicit destructor call or call to std::destroy_at
. Then you need to manually deallocate the memory with operator delete
/operator delete[]
.
You are trying to apply C patterns to C++ that offer no benefit in C++.
The flexible array member is not needed to implement what you want. The user shouldn't be using a MyContainer*
to refer to your container instance. Instead MyContainer
should be used by-value by the user and MyContainer
should be a class type holding the pointer to the dynamically allocated memory. Whether the i
member is held in MyContainer
or at the beginning of the allocation for the pointer it holds, is up to you.
Have a look at how standard library implementations implement std::vector
. You are essentially trying to implement the same thing, with restricted feature set. std::vector
is type-generic, has strong exception safety guarantees, has better algorithmic complexity than what you are trying to do, works with generic allocators instead of only new
/delete
, etc.
Also, these implementations do not store the size at the beginning of the allocation, but in the container object itself, but that is a minor difference. Implementing such a container in C++ correctly is non-trivial and requires decent understanding of the object model.