Move of class with pimpl won't compile
Asked Answered
M

1

6

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.
Morgenthaler answered 26/2, 2014 at 16:45 Comment(5)
looks like you will need to move constructor implementation into the source fileWrought
Try removing this line C c2 = move(c);. Does it still compile?Hatred
I think that this is a duplicate https://mcmap.net/q/1011482/-how-does-the-custom-deleter-of-std-unique_ptr-workGenvieve
What's your compiler error?Cucumber
@Cucumber included compiler outputMorgenthaler
E
4

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.

Eliza answered 26/2, 2014 at 17:12 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.