Error when member has unique_ptr in c++ forward declaration (error: invalid application of 'sizeof' to an incomplete type 'B')

Asked 2 years ago, Updated 2 years ago, 87 views

There is a compilation error in unique_ptr, and I don't know even if I look into it, so please let me ask you a question.

A.h

#include<memory>
class B;

class A {

    public:
        static std::unique_ptr<A>create();

    private:
        void init();
        std::unique_ptr<B>_b;

};

A.cpp

#include "A.h"
# include "B.h"
std::unique_ptr<A>A::create(){
    std::unique_ptr<A>ptr(new A);
    ptr->init();
    return ptr;
}

void A::init(){
    _b=B::create();
}

B.h

#include<memory>

classB{
    public:
    static std::unique_ptr<B>create();
};

B.cpp

#include "B.h"

std::unique_ptr<B>B::create(){
        std::unique_ptr<B>ptr(newB);
        return ptr;
}

main.cpp

#include "A.h"

int main(void){
    auto a = A::create();
    return 0;
}

The following error will appear

    clang++ A.cpp B.cpp main.cpp -std=c++11
    In file included from main.cpp:1:
    In file included from ./A.h:1:
    /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include/c++/v1/memory:2395:27: error: invalid application of 'sizeof' to an incomplete type 'B'
                            static_assert(sizeof(_Tp) > 0, "default_delete can not delete incomplete type");
                                                        ^~~~~~~~~~~
    /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include/c++/v1/memory:2603:13: note: in instantiation of member function 'std::__1::default_delete<B>::operator()' requested here
                            __ptr_.second()(__tmp);
                            ^
    /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include/c++/v1/memory:2571:46: note: in instantiation of member function 'std::__1::unique_ptr<B, std::__1::default_delete<B> >::reset' requested here
            _LIBCPP_INLINE_VISIBILITY ~unique_ptr() {reset();}
                                                                                             ^
    ./A.h:4:7: note: in instantiation of member function 'std::__1::unique_ptr<B, std::__1::default_delete<B> >::~unique_ptr' requested here
    class A {
                ^
    /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include/c++/v1/memory:2603:13: note: in instantiation of member function 'std::__1::default_delete<A>::operator()' requested here
                            __ptr_.second()(__tmp);
                            ^
    /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include/c++/v1/memory:2571:46: note: in instantiation of member function 'std::__1::unique_ptr<A, std::__1::default_delete<A> >::reset' requested here
            _LIBCPP_INLINE_VISIBILITY ~unique_ptr() {reset();}
                                                                                             ^
    main.cpp:4:12: note: in instantiation of member function 'std::__1::unique_ptr<A, std::__1::default_delete<A> >::~unique_ptr' requested here
        auto a = A::create();
                         ^
    ./A.h:2:7:note:forward declaration of 'B'
    class B;
                ^
    1 error generated.

If you include B.h instead of B's forward declaration in A.h, the error will not appear, but
Is this the only way to do it?
If you change to shared_ptr, there is no error.

When I looked it up with this error, I found that there were only topics related to Pimple.

Thank you for your cooperation.

c++ c++11

2022-09-29 21:47

3 Answers

You can de-inline A's destructor as shown in the answer related to Simple.

A.h

#include<memory>
class B;

class A {

    public:
        static std::unique_ptr<A>create();
        ~A(); // De-Inline Destructors

    private:
        void init();
        std::unique_ptr<B>_b;

};

A.cpp

#include "A.h"
# include "B.h"
std::unique_ptr<A>A::create(){
    std::unique_ptr<A>ptr(new A);
    ptr->init();
    return ptr;
}

A::~A()=default;//Destructor de-inline

void A::init(){
    _b=B::create();
}

Note:


2022-09-29 21:47

All STL std::unique_ptr<T> are defined inline as template classes, and delete T ultimately calls the T destructor.Therefore, T (B in the example question) must be defined and the destructor must be accessible.

There is also a way to use Pimpl, but if that's the case, separate it from the declaration without inline definition somewhere. If you want to narrow the scope of influence, do you want to separate std::default_delete<B>::operator()?

std::unique_ptr<B> in A.h.

namespace std{
    template<>struct default_delete<B>{
        constexpr default_delete()noexcept=default;
        void operator() (B*ptr) const noxcept;
    };
}

Specialize with and use B.cpp to

 void std::default_delete<B>::operator()(B*ptr)const noxcept {deleteptr;}

can be defined as to separate dependencies with class B.


2022-09-29 21:47

As far as the description of unique_ptr is concerned, the default_delete<T> class, which is the second argument D of the template, seems to require that T be complete.

Quoted from default_delete<T> description

If type T is incomplete, operator() execution is ineligible.

Therefore, I think it is a specification that the template T in unique_ptr cannot be declared forward.


2022-09-29 21:47

If you have any answers or tips


© 2024 OneMinuteCode. All rights reserved.