unique_ptr,pimpl/forward声明和完整定义 [英] unique_ptr, pimpl/forward declaration and complete definition

查看:139
本文介绍了unique_ptr,pimpl/forward声明和完整定义的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我已经在此处

I already checked out the questions here and here, but still cannot figure out what is wrong.

这是调用代码:

#include "lib.h"

using namespace lib;

int
main(const int argc, const char *argv[]) 
{
    return 0;
}

这是lib代码:

#ifndef lib_h
#define lib_h

#include <string>
#include <vector>
#include <memory>

namespace lib
{

class Foo_impl;

class Foo
{
    public:
        Foo();
        ~Foo();

    private:
        Foo(const Foo&);
        Foo& operator=(const Foo&);

        std::unique_ptr<Foo_impl> m_impl = nullptr;

        friend class Foo_impl;
};

} // namespace

#endif

clang ++给我这个错误:

clang++ gives me this error:

'sizeof'对无效类型'lib :: Foo_impl'的无效应用
注意:在实例化成员函数'std :: default_delete :: operator()'时

invalid application of 'sizeof' to an incomplete type 'lib::Foo_impl'
note: in instantiation of member function 'std::default_delete::operator()' requested

您可以看到我已经明确声明了Foo析构函数.我在这里还想念什么?

You can see I already specifically declared Foo destructor. What else am I missing here?

推荐答案

Foo_impl的实现必须在std::unique_ptr<Foo_impl> m_impl = nullptr中要求的实例化之前完成.

The implementation of Foo_impl must be complete prior to the instantiation required in std::unique_ptr<Foo_impl> m_impl = nullptr.

保留声明的类型(但不进行初始化)将解决错误(std::unique_ptr<Foo_impl> m_impl;),然后稍后需要在代码中对其进行初始化.

Leaving the type declared (but not initialised) will fix the error (std::unique_ptr<Foo_impl> m_impl;), you would then need to initialise it later on in the code.

您看到的错误来自用于测试此技术的技术的实现;不完整的类型.基本上,sizeof会导致仅向前声明的类型产生错误(即,在代码/编译时在该点使用时缺少定义).

The error you are seeing is from the implementation of a technique used to test for this; the incomplete type. Basically, sizeof will result in an error with types that are only forward declared (i.e. lack definition when used at that point in the code/compilation).

这里可能的解决方法如下;

A possible fix here would look like;

class Foo_impl;

class Foo
{
  // redacted
  public:
    Foo();
    ~Foo();

  private:
    Foo(const Foo&);
    Foo& operator=(const Foo&);

    std::unique_ptr<Foo_impl> m_impl;// = nullptr;
};

class Foo_impl {
  // ...
};

Foo::Foo() : m_impl(nullptr)
{
}

为什么需要完整的类型?

通过= nullptr实例化使用复制初始化,并要求构造函数和要声明的析构函数(对于unique_ptr<Foo_impl>).析构函数需要unique_ptr的deleter函数,默认情况下,该函数在指向Foo_impl的指针上调用delete,因此它需要析构函数Foo_impl,并且Foo_impl的析构函数未在不完整的声明中声明.类型(编译器不知道它是什么样子).另请参见霍华德的答案.

The instantiation via = nullptr uses copy initialisation and requires the constructor and destructor to be declared (for unique_ptr<Foo_impl>). The destructor requires the deleter function of the unique_ptr which, by default, calls delete on the pointer to Foo_impl hence it requires the destructor of Foo_impl, and the destructor of Foo_impl is not declared in the incomplete type (the compiler doesn't know what it looks like). See Howard's answer on this as well.

关键是在不完整类型上调用delete会导致未定义的行为(第5.3.5/5节),因此在unique_ptr的实现中进行了明确检查.

Key here is that calling delete on an incomplete type results in undefined behaviour (§ 5.3.5/5) and hence is explicitly checked for in the implementation of unique_ptr.

这种情况的另一种替代方法可能是使用直接初始化,如下所示;

Another alternative for this situation may be to use direct initialisation as follows;

std::unique_ptr<Foo_impl> m_impl { nullptr };

关于非静态数据成员初始化程序(NSDMI)以及至少在这是否是需要成员定义的上下文,似乎存在一些争论.对于clang(可能还有gcc),这似乎就是这样的上下文.

There seems to be some debate on the non-static data member initialiser (NSDMI) and whether this is a context that requires the member definition to exist, at least for clang (and possibly gcc), this seems to be such a context.

这篇关于unique_ptr,pimpl/forward声明和完整定义的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆