2

我使用的是 PIMPL 成语,特别是我使用的是这篇文章提供的模板。鉴于下面的一组类并使用 VS2015 Update 3 进行编译,我得到了编译错误:

错误 C2027 使用未定义类型“C::C_impl”(编译源文件 src\A.cpp)

错误 C2338 无法删除不完整的类型(编译源文件 src\A.cpp)

警告 C4150 删除指向不完整类型“C::C_impl”的指针;没有调用析构函数(编译源文件 src\A.cpp)

我可以通过取消注释来解决这个问题C::~C(),这让我相信某些东西正在阻止~C()自动生成,但我不明白是什么。根据此参考,如果以下任何一项为真,则类型 T 的析构函数被隐式定义为已删除:

  1. T 有一个不能被破坏的非静态数据成员(已删除或不可访问的析构函数)
  2. T 具有无法破坏的直接或虚拟基类(已删除或不可访问的析构函数)
  3. T 是一个联合,并且有一个带有非平凡析构函数的变体成员。
  4. 隐式声明的析构函数是虚拟的(因为基类有一个虚拟析构函数),并且查找释放函数(运算符 delete() 会导致调用模棱两可、已删除或不可访问的函数。

第 2、3 和 4 项显然不适用于C,而且我不认为第 1 项适用,因为pimpl<>(C的唯一成员) 明确定义了析构函数。

有人可以解释发生了什么吗?

#pragma once
#include <Pimpl.h>

class A
{
private:
    struct A_impl;
    pimpl<A_impl> m_pimpl;
};

溴化氢

#pragma once
#include "C.h"

class B
{
private:
    C m_C;
};

通道

#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 实现:

粉刺.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(); }

关于上面代码的几点说明:

  • 我正在尝试向我的图书馆的消费者公开AC保持B内部信息。
  • 这里没有B.cpp,它会是空的。
4

2 回答 2

3

您必须在定义 C::C_impl 之后手动定义 C 析构函数,因为默认编译器会尝试在使用点生成 C 析构函数,但它可能是找不到 C::C_impl 定义的点(例如,它可以是B.cpp)。

于 2016-07-21T14:27:22.297 回答
1

我认为问题在于std::unique_ptr需要在实例化时了解破坏函数(即析构函数)。

使用默认析构函数~C(),编译器会在使用时生成它,这意味着它正在尝试删除pimpl<C_impl>具有可用信息的对象A.cpp。当然,因为C_impl仅在此时声明,编译器不知道如何销毁C_impl对象,这就是您收到的错误。

取消注释~C();告诉编译器不要担心如何销毁 a C_impl,它将在其他地方定义。在您的情况下,它是在已知定义的C.cpp地方定义的C_impl

作为对您的编辑的回应,pimpl<C_impl>具有std::unique_ptr<C_impl>非静态数据成员的析构函数具有不可访问的析构函数(C_impl在使用点上是不完整的类型 withing A.cpp)。

于 2016-07-21T14:27:06.033 回答