Search code examples
c++memory-managementdynamicflexible-array-member

How to resolve the "unknown array size in delete" error when using a class with a flexible array member (FAM) and a non-trivial destructor?


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

Solution

  • 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.