I'm using the PIMPL idiom, and specifically I'm using the template provided from this post. Given the set of classes below and compiling with VS2015 Update 3, I'm getting compile errors:
Error C2027 use of undefined type 'C::C_impl' (compiling source file src\A.cpp)
Error C2338 can't delete an incomplete type (compiling source file src\A.cpp)
Warning C4150 deletion of pointer to incomplete type 'C::C_impl'; no destructor called (compiling source file src\A.cpp)
I can resolve this by uncommenting C::~C()
, which leads me to believe that something is preventing ~C()
from being automatically generated but I don't understand what. According to this reference, the destructor for type T is implicitly defined as deleted if any of the following is true:
- T has a non-static data member that cannot be destructed (has deleted or inaccessible destructor)
- T has direct or virtual base class that cannot be destructed (has deleted or inaccessible destructors)
- T is a union and has a variant member with non-trivial destructor.
- The implicitly-declared destructor is virtual (because the base class has a virtual destructor) and the lookup for the deallocation function (operator delete() results in a call to ambiguous, deleted, or inaccessible function.
Items #2, 3, and 4 obviously do not apply to C
, and I don't believe that #1 applies because pimpl<>
(C
's only member) defines a destructor explicitly.
Can someone please explain what's going on?
A.h
#pragma once
#include <Pimpl.h>
class A
{
private:
struct A_impl;
pimpl<A_impl> m_pimpl;
};
B.h
#pragma once
#include "C.h"
class B
{
private:
C m_C;
};
C.h
#pragma once
#include <Pimpl.h>
class C
{
public:
// Needed for the PIMPL pattern
//~C();
private:
struct C_impl;
pimpl<C_impl> m_pimpl;
};
A.cpp
#include <memory>
#include "A.h"
#include "B.h"
#include <PimplImpl.h>
struct A::A_impl
{
std::unique_ptr<B> m_pB;
};
// Ensure all the code for the template is compiled
template class pimpl<A::A_impl>;
C.cpp
#include <C.h>
#include <PimplImpl.h>
struct C::C_impl { };
// Needed for the PIMPL pattern
//C::~C() = default;
// Ensure all the code for the template is compiled
template class pimpl<C::C_impl>;
For completeness, the PIMPL implementation from the post referenced above:
pimpl.h
#pragma once
#include <memory>
template<typename T>
class pimpl
{
private:
std::unique_ptr<T> m;
public:
pimpl();
template<typename ...Args> pimpl(Args&& ...);
~pimpl();
T* operator->();
T& operator*();
};
PimplImpl.h
#pragma once
#include <utility>
template<typename T>
pimpl<T>::pimpl() : m{ new T{} } {}
template<typename T>
template<typename ...Args>
pimpl<T>::pimpl(Args&& ...args)
: m{ new T{ std::forward<Args>(args)... } }
{
}
template<typename T>
pimpl<T>::~pimpl() {}
template<typename T>
T* pimpl<T>::operator->() { return m.get(); }
template<typename T>
T& pimpl<T>::operator*() { return *m.get(); }
A few notes about the code above:
- I'm trying to expose
A
andC
to consumers of my library and keepB
internal. - There is no B.cpp here, it would be empy.