Have you ever wonder how to implement a shared_ptr
? This item is to make you think about how to implement the behavior that implicit pointer conversion provided by porlymorphism.
1 | class Top {}; |
You will be able to create derivated class pointer and assign it to base pointer, or assign a non-const pointer to a const pointer.
Now it’s your time to think about how to implement such behavior for SmartPtr
so following operations are legal.
1 | SmartPtr<Top> pt1 = SmartPtr<Middle>(new Middle); |
The first attempt will be
1 | template<typename T> |
And you will immediate realize that this won’t work. Because SmartPtr<Middle>
and SmartPtr<Top>
as completely different classes, no more closely related than, say, vector<float>
and Widget
. In order to make it work following this route, we have to implement conversion functions for every possible conversion we need to support. In principle, the number of constructors we need is unlimited. Because people will add derived class on top of current class and you don’t want to change SmartPtr
‘s implementation in order to support new derived class.
Clearly we need a better way, which is member templates
, or in this case generalized copy constructors
.
1 | template<typename T> |
With this, we achieved what we want previously, to make following compile
1 | SmartPtr<Top> pt1 = SmartPtr<Middle>(new Middle); |
But wait a second. Follwing code will also compile which is complete nonsense
1 | SmartPtr<Middle> pt1 = SmartPtr<Top>(new Middle); |
How do we apply restriction to our SmartPtr
so only meaningful implicit type conversion is allowed? We need to rely on existing non-template verion of implicit type conversion. Here is how
1 | template<typename T> |
Basically we accept all types in our copy constructor but rely on non-template implicit conversion to accept all compatible types. If we pass in an noncompatible type, compiler will give us error preventing us from doing so. Pretty neat right?
In fact, this is a very common technique to implement generalized copy constructor
and generalized assignment operator
.
One last thing to remember is, even you provide generalized copy constructor
and generalized assignment operator
, you still need to provide default copy constructor
and default assignment operator
. Otherwise compiler generated default will be used.
So end result looks like following
1 | template<typename T> |