Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Move of class with pimpl won't compile

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.
like image 481
piotr Avatar asked Feb 26 '14 16:02

piotr


1 Answers

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.

like image 191
Mark B Avatar answered Nov 07 '22 10:11

Mark B