pimpl 的 Unique_ptr 用法 - 即使声明了析构函数也不会编译

2024-01-03

我正在尝试使用 unique_ptr 作为 pimpl 习惯用法。因此,我在类内部声明了一个析构函数,以便在未定义 impl 类的情况下不会实例化 unique_ptr 删除,然后我在另一个文件中定义它。

这是我的布局:

包装器.h:

#pragma once
#include <memory>

struct Wrapper
{
    class Impl;
    ~Wrapper();
    std::unique_ptr<Impl> _impl;
};

包装器.cpp:

#include "wrapper.h"

class Wrapper::Impl {};
Wrapper::~Wrapper() = default;

这个文件编译得很好。 但是,在编译 main.cpp 时,我收到不完整的类型错误(请参阅下面的错误):

主要.cpp:

#include "wrapper.h"

int main()
{
    Wrapper w;
    return 0;
}

但是,如果我将wrapper.cpp 中的两行添加到main.cpp 的末尾,则它可以正常编译。 我不明白两件事:

  1. 为什么错误首先会发生?我认为在类中声明析构函数会移动删除调用点?
  2. 怎么添加代码在最后main.cpp 文件的内容会影响此错误吗?我想“如果使用默认删除器,T必须完整在代码中的点删除器被调用的地方,发生在析构函数中”(来自参考参数 https://en.cppreference.com/w/cpp/memory/unique_ptr).

我缺少什么?

UPDATE:

按照@AdrianMole的建议,我在类Wrapper定义中添加了一个ctor声明,并且由于某种原因它修复了错误,即使错误(和unique_ptr规范)引用了析构函数。

更新了wrapper.h:

struct Wrapper
{
    class Impl;
    Wrapper();
    ~Wrapper();
    std::unique_ptr<Impl> _impl;
};

所以我补充一个问题:

  1. 为什么添加构造函数声明可以解决这个问题?

这些是我在 MSVC 中遇到的错误,但在 clang 或 gcc 中也会出现类似的错误(我尝试过在线编译器):

内存(2536,1):错误C2027:使用未定义类型“Wrapper::Impl”

wrapper.h(7): message : 请参阅“Wrapper::Impl”的声明

内存(2535):消息:编译类模板成员函数'void std::default_deleteWrapper::Impl::operator ()(_Ty *) noexcept const'

    with

    [

        _Ty=Wrapper::Impl

    ]

内存(2647):消息:请参阅正在编译的函数模板实例化“void std::default_deleteWrapper::Impl::operator ()(_Ty *) no except const”的引用

    with

    [

        _Ty=Wrapper::Impl

    ]

内存(2574):消息:请参阅正在编译的类模板实例化“std::default_deleteWrapper::Impl”的引用

wrapper.h(9): message : 请参阅正在编译的类模板实例化 'std::unique_ptrWrapper::Impl,std::default_delete<:impl>' 的引用

内存(2536,25):错误C2338:无法删除不完整的类型

内存(2537,1):警告C4150:删除指向不完整类型“Wrapper::Impl”的指针;没有调用析构函数

wrapper.h(7): message : 请参阅“Wrapper::Impl”的声明


任何构造函数定义(包括隐式定义的默认构造函数)都可能调用类类型的所有成员对象的类型的析构函数。这样做的理由是,如果构造后面的成员抛出异常,则需要调用所有先前成员的析构函数。对于最后一个成员,这不是严格要求的,但标准没有例外。

例如,如果您的班级是:

struct complete_type_with_throwing_constructor {
    complete_type_with_throwing_constructor() { throw 0; }
};

struct Wrapper
{
    class Impl;
    ~Wrapper();
    std::unique_ptr<Impl> _impl;
    complete_type_with_throwing_constructor x;
};

然后是隐式默认构造函数Wrapper将构建_impl然后在默认构造函数之后销毁它x throws.

这就是如果您显式声明默认构造函数,您的代码可以工作的原因。这将定义移动到析构函数的位置std::unique_ptr<Impl>可以被实例化。


至于你的第二个问题,你是对的,之后添加定义时代码仍然不起作用。只是编译器没有检测到它,所以他们没有错误。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

pimpl 的 Unique_ptr 用法 - 即使声明了析构函数也不会编译 的相关文章

随机推荐