本文介绍了是 std::unique_ptr<T>需要知道 T 的完整定义吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在标题中有一些代码,如下所示:

I have some code in a header that looks like this:

#include <memory>

class Thing;

class MyClass
{
    std::unique_ptr< Thing > my_thing;
};

如果我在不包含 Thing 类型定义的 cpp 中包含这个头文件,那么这不会在 VS2010-SP1 下编译:

If I include this header in a cpp that does not include the Thing type definition, then this does not compile under VS2010-SP1:

1>C:Program Files (x86)Microsoft视觉工作室10.0VCincludememory(2067): 错误 C2027: 使用未定义类型 'Thing'

std::shared_ptr 替换 std::unique_ptr 并编译.

Replace std::unique_ptr by std::shared_ptr and it compiles.

所以,我猜是当前 VS2010 std::unique_ptr 的实现需要完整定义,并且完全依赖于实现.

So, I'm guessing that it's the current VS2010 std::unique_ptr's implementation that requires the full definition and it's totally implementation-dependant.

或者是吗?它的标准要求中是否有某些内容使 std::unique_ptr 的实现无法仅使用前向声明?感觉很奇怪,因为它应该只保存一个指向 Thing 的指针,不是吗?

Or is it? Is there something in it's standard requirements that makes impossible for std::unique_ptr's implementation to work with a forward declaration only? It feels strange as it should only hold a pointer to Thing, shouldn't it?

推荐答案

此处采用.

C++ 标准库中的大多数模板都要求使用完整类型实例化它们.然而,shared_ptrunique_ptr部分例外.一些(但不是全部)成员可以用不完整的类型实例化.这样做的动机是使用智能指针支持诸如 pimpl 之类的习语,并且不会冒未定义行为的风险.

Most templates in the C++ standard library require that they be instantiated with complete types. However shared_ptr and unique_ptr are partial exceptions. Some, but not all of their members can be instantiated with incomplete types. The motivation for this is to support idioms such as pimpl using smart pointers, and without risking undefined behavior.

当您有一个不完整的类型并在其上调用 delete 时,可能会发生未定义的行为:

Undefined behavior can occur when you have an incomplete type and you call delete on it:

class A;
A* a = ...;
delete a;

以上为合法代码.它会编译.您的编译器可能会也可能不会对上述代码发出警告.当它执行时,可能会发生不好的事情.如果你很幸运,你的程序会崩溃.然而,更可能的结果是你的程序会悄悄地泄漏内存,因为 ~A() 不会被调用.

The above is legal code. It will compile. Your compiler may or may not emit a warning for above code like the above. When it executes, bad things will probably happen. If you're very lucky your program will crash. However a more probable outcome is that your program will silently leak memory as ~A() won't be called.

在上面的例子中使用 auto_ptr 没有帮助.您仍然会得到与使用原始指针相同的未定义行为.

Using auto_ptr<A> in the above example doesn't help. You still get the same undefined behavior as if you had used a raw pointer.

尽管如此,在某些地方使用不完整的类还是很有用的!这是 shared_ptrunique_ptr 帮助的地方.使用这些智能指针之一将使您摆脱不完整的类型,除非有必要拥有完整的类型.最重要的是,当需要一个完整的类型时,如果你在那个时候尝试使用一个不完整类型的智能指针,你会得到一个编译时错误.

Nevertheless, using incomplete classes in certain places is very useful! This is where shared_ptr and unique_ptr help. Use of one of these smart pointers will let you get away with an incomplete type, except where it is necessary to have a complete type. And most importantly, when it is necessary to have a complete type, you get a compile-time error if you try to use the smart pointer with an incomplete type at that point.

不再有未定义的行为:

如果您的代码可以编译,那么您已经在需要的任何地方使用了完整类型.

If your code compiles, then you've used a complete type everywhere you need to.

class A
{
    class impl;
    std::unique_ptr<impl> ptr_;  // ok!

public:
    A();
    ~A();
    // ...
};

shared_ptrunique_ptr 在不同的地方需要一个完整的类型.原因很模糊,与动态删除器与静态删除器有关.确切的原因并不重要.事实上,在大多数代码中,确切地知道需要完整类型的位置对您来说并不重要.只是代码,如果你弄错了,编译器会告诉你.

shared_ptr and unique_ptr require a complete type in different places. The reasons are obscure, having to do with a dynamic deleter vs a static deleter. The precise reasons aren't important. In fact, in most code it isn't really important for you to know exactly where a complete type is required. Just code, and if you get it wrong, the compiler will tell you.

但是,如果对您有帮助,这里有一张表格,其中记录了 shared_ptrunique_ptr 的几个成员关于完整性要求.如果成员需要完整类型,则条目为C",否则表条目为I".

However, in case it is helpful to you, here is a table which documents several members of shared_ptr and unique_ptr with respect to completeness requirements. If the member requires a complete type, then entry has a "C", otherwise the table entry is filled with "I".

Complete type requirements for unique_ptr and shared_ptr

                            unique_ptr       shared_ptr
+------------------------+---------------+---------------+
|          P()           |      I        |      I        |
|  default constructor   |               |               |
+------------------------+---------------+---------------+
|      P(const P&)       |     N/A       |      I        |
|    copy constructor    |               |               |
+------------------------+---------------+---------------+
|         P(P&&)         |      I        |      I        |
|    move constructor    |               |               |
+------------------------+---------------+---------------+
|         ~P()           |      C        |      I        |
|       destructor       |               |               |
+------------------------+---------------+---------------+
|         P(A*)          |      I        |      C        |
+------------------------+---------------+---------------+
|  operator=(const P&)   |     N/A       |      I        |
|    copy assignment     |               |               |
+------------------------+---------------+---------------+
|    operator=(P&&)      |      C        |      I        |
|    move assignment     |               |               |
+------------------------+---------------+---------------+
|        reset()         |      C        |      I        |
+------------------------+---------------+---------------+
|       reset(A*)        |      C        |      C        |
+------------------------+---------------+---------------+

任何需要指针转换的操作都需要 unique_ptrshared_ptr 的完整类型.

Any operations requiring pointer conversions require complete types for both unique_ptr and shared_ptr.

unique_ptr{A*} 构造函数可以避免不完整的 A 仅当编译器不需要设置对 的调用时~unique_ptr().例如,如果您将 unique_ptr 放在堆上,则可以避免使用不完整的 A.关于这一点的更多详细信息可以在 BarryTheHatchet 的 回答 此处.

The unique_ptr<A>{A*} constructor can get away with an incomplete A only if the compiler is not required to set up a call to ~unique_ptr<A>(). For example if you put the unique_ptr on the heap, you can get away with an incomplete A. More details on this point can be found in BarryTheHatchet's answer here.

这篇关于是 std::unique_ptr&lt;T&gt;需要知道 T 的完整定义吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

08-29 08:10