Is it true that a unique_ptr declaration, unlike a auto_ptr declaration, is well-defined when its template type is of an incomplete type?

只愿长相守 提交于 2019-12-01 02:55:28

According to Herb Sutter in GOTW #100, unique_ptr suffers from the same problem as auto_ptr with respect to incomplete types.

...although both unique_ptr and shared_ptr can be instantiated with an incomplete type, unique_ptr’s destructor requires a complete type in order to invoke delete...

His suggestion is to declare the destructor of your containing class (i.e. T1) in the header file, then place it's definition in a translation unit in which T2 is a complete type.

// T1.h
struct T2;

struct T1
{
  ~T1();
  std::unique_ptr< T2 >;
};

// T1.cpp
#include "T2.h"

T1::~T1()
{
}

The following example is an attempt to demonstrate the difference between std::auto_ptr<T> and std::unique_ptr<T>. First consider this program consisting of 2 source files and 1 header:

The header:

// test.h

#ifndef TEST_H
#define TEST_H

#include <memory>

template <class T>
using smart_ptr = std::auto_ptr<T>;

struct T2;

struct T1
{
    smart_ptr<T2> obj;

    T1(T2* p);
};

T2*
source();

#endif  // TEST_H

First source:

// test.cpp

#include "test.h"

int main()
{
    T1 t1(source());
}

Second source:

// test2.cpp

#include "test.h"
#include <iostream>


struct T2
{
    ~T2() {std::cout << "~T2()\n";}
};

T1::T1(T2* p)
    : obj(p)
{
}

T2*
source()
{
    return new T2;
}

This program should compile (it may compile with a warning, but it should compile). But at run time it demonstrates undefined behavior. And it probably won't output:

~T2()

which indicates that T2's destructor has not been run. At least it doesn't on my system.

If I change test.h to:

template <class T>
using smart_ptr = std::unique_ptr<T>;

Then the compiler is required to output a diagnostic (an error).

That is, when you make this mistake with auto_ptr you get a run time error. When you make this mistake with unique_ptr you get a compile time error. And that is the difference between auto_ptr and unique_ptr.

To fix the compile time error you must outline ~T1() after T2 is complete. In test2.cpp add after T2:

T1::~T1() = default;

Now it should compile and output:

~T2()

You will likely want to declare and outline move members as well:

T1::T1(T1&&) = default;
T1& T1::operator=(T1&&) = default;

You could make these same fixes with auto_ptr and it would again be correct. But again, the difference between auto_ptr and unique_ptr is that with the former, you don't find out until run time that you have some debugging to do (modulo optional warnings your compiler may give). With the latter you are guaranteed to find out at compile time.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!