Home > Mobile >  pImpl pattern in C needs complete definition of impl subclass
pImpl pattern in C needs complete definition of impl subclass

Time:12-15

I've looked at many of the SO questions on pImpl, unique_ptr and forward declarations, but can't figure out what is wrong. The answer at Is std::unique_ptr<T> required to know the full definition of T? seems very complete, but I can't figure out how to apply it to my situation.

I have a simple pImpl pattern at https://wandbox.org/permlink/9joq3PnkEZ6j8TI8. Main class contains a unique_ptr to the impl class, which manages some resources so it has custom constructors/destructors. I'm using C 20 with clang 14 (on MacOS, as it happens, but the same error happens in the wandbox above).

Compiling it gives the dreaded "invalid application of 'sizeof' to an incomplete type" error.

The code is this:

// testclass.h
#include <experimental/propagate_const>

// Main class with a pImpl pattern -- impl class is incomplete here.
class Main {
  public:
    void main_f();
    Main();
  private:
    class impl;
    std::experimental::propagate_const<std::unique_ptr<impl>> pImpl;
};
// testclass.cpp
#include "testclass.h"

// Implementation of Main with its impl

class Main::impl {
  private:
    int foo;
 public:
  impl() = default;
  ~impl() {
    cout << "bye from impl";
  };
  impl(const impl &) = delete;
  impl &operator=(const impl &other) = delete;
  impl(impl &&source) noexcept = default;
  impl &operator=(impl &&other) noexcept = default;
}

// To construct a Main, construct a unique pointer to a new impl
Main::Main() : pImpl{std::make_unique<impl>()} {}

and main:

// main.cpp
#include "testclass.h"
int main() { 
    Main main;
    return 0;
}

Can anyone point me in the right direction?

CodePudding user response:

You need to declare the destructor ~Main(); in the class, and define it in the .cpp file.

That's because its definition requires impl to be complete, and now it's getting implicitly defined when you create Main main;, and there impl isn't complete.

You might want to do the same for the move operations, since they're not defined automatically because of a custom destructor.

  • Related