Question

In the following example, how is it possible that ~CImpl is called correctly but when the class needs to be moved, the compiler says it has an incomplete type?

If the declaration of Impl is moved to the header it works, my question is how come the destructor is called fine so it doesn't seem that the type is incomplete, but the problem appears when moving.

file: C.hpp

#include <memory>

class Impl;


class C
{
public:
    C();
    ~C();

    C(C&&) = default;
    C& operator=(C&&) = default;

    std::unique_ptr<Impl> p;
};

file C.cpp

#include "C.hpp"
#include <iostream>

using namespace std;

class Impl
{
public:
    Impl() {}
    virtual ~Impl() = default;
    virtual void f() = 0;
};

class CImpl: public Impl
{
public:
    ~CImpl()
    {
        cout << "~CImpl()" << endl;
    }
    void f()
    {
        cout << "f()" << endl;
    }

};


C::C():
    p(new CImpl())
{}

C::~C()

file: main.cpp

#include <iostream>
#include <vector>
#include "C.hpp"
using namespace std;
int main(int argc, char *argv[])
{
    vector<C> vc;
    // this won't compile
    //vc.emplace_back(C());
    C c;
    C c2 = move(c); // this won't compile
}

Compiler output:

+ clang++ -std=c++11 -Wall -c C.cpp
+ clang++ -std=c++11 -Wall -c main.cpp
In file included from main.cpp:3:
In file included from ./C.hpp:1:
In file included from /usr/bin/../lib/gcc/x86_64-linux-gnu/4.8/../../../../include/c++/4.8/memory:80:
/usr/bin/../lib/gcc/x86_64-linux-gnu/4.8/../../../../include/c++/4.8/bits/unique_ptr.h:65:16: error: invalid application of 'sizeof' to an incomplete type 'Impl'
        static_assert(sizeof(_Tp)>0,
                      ^~~~~~~~~~~
/usr/bin/../lib/gcc/x86_64-linux-gnu/4.8/../../../../include/c++/4.8/bits/unique_ptr.h:184:4: note: in instantiation of member function
      'std::default_delete<Impl>::operator()' requested here
          get_deleter()(__ptr);
          ^
./C.hpp:12:5: note: in instantiation of member function 'std::unique_ptr<Impl, std::default_delete<Impl> >::~unique_ptr' requested here
    C(C&&) = default;
    ^
./C.hpp:3:7: note: forward declaration of 'Impl'
class Impl;
      ^
1 error generated.
Was it helpful?

Solution

Your destructor works fine because the (empty) body of the destructor is in the C source file with access to the full definition of Impl. The move constructor and move assignment however are defaulted (defined) in the header with NO definition of Impl.

What you can do is C(C&&); in the header and C::C(C&&) = default; in the source file.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top