I wrote this article and got some comments on it that confused me.
It basically boils down to my having seen T2
used only as a template parameter and mi
The following example is an attempt to demonstrate the difference between std::auto_ptr
and std::unique_ptr
. First consider this program consisting of 2 source files and 1 header:
The header:
// test.h
#ifndef TEST_H
#define TEST_H
#include
template
using smart_ptr = std::auto_ptr;
struct T2;
struct T1
{
smart_ptr 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
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
using smart_ptr = std::unique_ptr;
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.