const overloads do not really work for smart-pointers. They are an illusion.
Let's have a reference-counted smart-pointer. I will call it SharedPtr<T> for now. This applies to boost as well as to any you and I are likely to implement.
Now, somebody I have const SharedPtr<T> &. So you might think I can only call const methods on my T. I think not. What I have is actually comparable to a T* const, not a const T*.
Why? Because T is const within the pointer and not what it points to.
And suppose I invoke a simple copy-constructor.
Code:
const SharedPtr<T> & spt1;
SharedPtr<T> spt2( spt1 ); // perfectly legal, normal copy constructor
So now I have suddenly created a brand new SharedPtr, pointing to the same T but clearly this one is not const.
If I want a shared pointer to a const T then I must use
Code:
SharedPtr< const T >
.
Incidentally, do your smart pointers have templated copy-constructors?
Code:
template <typename T2>
SharedPtr<T>( const SharedPtr< T2 > & rhs );
(Note, you should also specialise it for your own T because the compiler may pick the default as the best match, not the templated one).
This should be implemented to create a SharedPtr<T> from a SharedPtr<T2> when and only when you can automatically cast from T2 to T, i.e. when T is const T2 or when T is a base class of T2.
(Another reason perhaps to use boost and not try to write your own, but it is instructive to go through these points).
So now back to the earlier point. When we are going to modify our T we want to make a copy. But your overload is based on having a const SharedPtr<T> not a SharedPtr< const T >. (How would your template know if it had a const argument anyway?).
In addition, it could well be that we have a non-const T pointer, but are only going to call const methods on it. Nothing wrong with that and we don't want to have to const_cast it to const (you can use const_cast that way too) just to get it to call the right overload. (Note that std::map has that effect too with operator[], and it can be messy if you want to look up that way without creating a new element, so it is better not to use the const-overload of std::map::operator[]).
Basically, we can rely on our client code using the correct function to get a non-const reference/pointer when it is going to modify, and a const reference/pointer when it is not. When it calls the non-const reference/pointer method we can then clone if necessary. The compiler will trap any occasion where you call the const method then try to modify.
By the way, operator* on a SmartPtr<T> usually returns a T& (or a const T&). It is an invalid operator when the pointer is NULL. And you might wish to handle the NULL case better.