我使用PIMPL习语,特别是我使用this post提供的模板。鉴于下面的一组类并使用VS2015 Update 3进行编译,我收到了编译错误:
错误C2027使用未定义类型' C :: C_impl' (编译源文件src \ A.cpp)
错误C2338无法删除不完整的类型(编译源文件src \ A.cpp)
警告C4150删除指向不完整类型的指针' C :: C_impl&#39 ;;没有析构函数调用(编译源文件src \ A.cpp)
我可以通过取消注释C::~C()
来解决这个问题,这会让我相信某些事情会阻止~C()
自动生成但我不明白什么。根据{{3}},如果满足以下任何条件,则类型T的析构函数被隐式定义为已删除:
- T有一个非静态数据成员,无法被破坏(已删除或无法访问析构函数)
- T具有无法销毁的直接或虚拟基类(已删除或无法访问的析构函数)
- T是一个联合体,并且具有带有非平凡析构函数的变体成员。
- 隐式声明的析构函数是虚拟的(因为基类具有虚拟析构函数),并且查找解除分配函数(operator delete()会导致调用不明确,已删除或无法访问的函数。
醇>
项目#2,3和4显然不适用于C
,我不相信#1适用,因为pimpl<>
(C
&#39; s only member)明确定义析构函数。
有人可以解释一下发生了什么吗?
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>;
为完整起见,上面引用的帖子中的PIMPL实现:
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
和C
,并将B
保留在内部。答案 0 :(得分:3)
您必须在定义C :: C_impl之后手动定义C析构函数,因为默认编译器尝试在使用点生成C析构函数,但它可能是无法找到C :: C_impl定义的点(例如,它可以是B.cpp)。
答案 1 :(得分:1)
我认为问题在于std::unique_ptr
需要在实例化时知道销毁函数(即析构函数)。
使用默认的析构函数~C()
,编译器会在使用点生成它,这意味着它正在尝试使用pimpl<C_impl>
中可用的信息删除A.cpp
对象。当然,由于C_impl
仅在此时被声明,编译器不知道如何销毁C_impl
对象,这就是您收到的错误。
取消注释~C();
告诉编译器不要担心如何销毁C_impl
,这将在其他地方定义。在C.cpp
中定义的C_impl
的定义已知的情况下。
为了响应您的编辑,pimpl<C_impl>
的析构函数具有std::unique_ptr<C_impl>
,其具有非静态数据成员且具有无法访问的析构函数(C_impl
在使用时是一个不完整的类型{ {1}})。