Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

QScopedPointer, boost::scoped_ptr - why complaining about incomplete types?

I have c-Structure that I want to embed in an cpp class without poisoning of my global namespace so I do not want to include the c-header.

That's why I want to use a smart scoped pointer (QScopedPointer or boost::scoped_ptr) with a forward declared structure name.

What I do not understand is the implementation of both mentioned scoped pointers that fails on compile:

boost:

error C2027: use of undefined type 'xxx'

template<class T> inline void checked_delete(T * x)
{
    // intentionally complex - simplification causes regressions
    typedef char type_must_be_complete[ sizeof(T)? 1: -1 ]; // < here
    (void) sizeof(type_must_be_complete);
    delete x;
}

and the same in Qt:

error C2027: use of undefined type 'xxx'

template <typename T>
struct QScopedPointerDeleter
{
    static inline void cleanup(T *pointer)
    {
        // Enforce a complete type.
        // If you get a compile error here, read the section on forward declared
        // classes in the QScopedPointer documentation.
        typedef char IsIncompleteType[ sizeof(T) ? 1 : -1 ]; // < here
        (void) sizeof(IsIncompleteType);

        delete pointer;
    }
};

The referenced documentation did not helped me. it says that the destructor of the forward declared class does not have to be inline and must be available at every possible instantiation of the cleanup of the scoped pointer. But my c-structure does not have an destructor.

So I have 2 questions:

  1. Why this check at all? Because it seems to be irrelevant to know the size for calling delete.
  2. How to deal with this?
like image 465
vlad_tepesch Avatar asked Sep 12 '14 09:09

vlad_tepesch


2 Answers

  1. The type of object being kept in smart pointer must be known in place where smart pointer is being destructed, so proper destructor of kept object can be called
  2. does your class (the one which holds smart pointer) have destructor? If no - add one in your cpp file. Otherwise compiler tries to add one when it sees your class definition and cannot do it because smart pointer's destructor tries to access unknown type.
like image 86
Michał Walenciak Avatar answered Sep 24 '22 21:09

Michał Walenciak


my c-Structure does not have a destructor.

For one thing, No. Your struct actually have a destructor - implicitly-declared destructor.


Anyway, let's go ahead.

delete pointer;

When compiling this code, we should call the destructor of *pointer. However, if *pointer is incomplete type, we can't know the destructor to call. In this case, standard [expr.delete] says it causes undefined behavior.

If the object being deleted has incomplete class type at the point of deletion and the complete class has a non-trivial destructor or a deallocation function, the behavior is undefined.

As you can see, if your struct doesn't have non-trivial destructor or a deallocation function (class-specific operator delete), it's not UB. However, you probably can add a destructor into your struct - you will do. If you do without fixing this point, it becomes really buggy bug. (Compiler doesn't have to report it; it's just UB, not illegal code.) So it's not considered as a good practice.

Because of this, deleting incomplete types is really what we should avoid. To avoid that, we uses this trick.

typedef char type_must_be_complete[ sizeof(T)? 1: -1 ]; // < here
(void) sizeof(type_must_be_complete);

Since sizeof(T) is illegal code if T is incomplete type, so it can reduce compile-time error before your program goes being crazy due to UB.

I highly recommend you to just include it despite slower compliation speed; Although your struct is trivial and doesn't have operator delete, they can be added without fixing, which causes UB.

like image 22
ikh Avatar answered Sep 26 '22 21:09

ikh