I was reading Top 10 dumb mistakes to avoid with C++11 smart pointer. Number #5 reads:
Mistake # 5 : Not assigning an object(raw pointer) to a shared_ptr as soon as it is created !
int main()
{
Aircraft* myAircraft = new Aircraft("F-16");
shared_ptr<aircraft> pAircraft(myAircraft);
...
shared_ptr<aircraft> p2(myAircraft);
// will do a double delete and possibly crash
}
and the recommendation is something like:
Use
make_shared
ornew
and immediately construct the pointer with it.
Ok, no doubt about it the problem and the recommendation.
However I have a question about the design of shared_ptr
.
This is a very easy mistake to make and the whole "safe" design of shared_ptr
could be thrown away by very easy-to-detect missuses.
Now the question is, could this be easily been fixed with an alternative design of shared_ptr
in which the only constructor from raw pointer would be that from a r-value reference?
template<class T>
struct shared_ptr{
shared_ptr(T*&& t){...basically current implementation...}
shared_ptr(T* t) = delete; // this is to...
shared_ptr(T* const& t) = delete; // ... illustrate the point.
shared_ptr(T*& t) = delete;
...
};
In this way shared_ptr
could be only initialized from the result of new
or some factory function.
Is this an underexploitation of the C++ language in the library? or What is the point of having a constructor from raw pointer (l-value) reference if this is going to be most likely a misuse?
Is this a historical accident? (e.g. shared_ptr was proposed before r-value references were introduced, etc) Backwards compatibility?
(Of course one could say std::shared_ptr<type>(std::move(ptr));
that that is easier to catch and also a work around if this is really necessary.)
Am I missing something?
I do not have any special insight into the design of shared_ptr
, but I think the most likely explanation is that the timelines involved made this impossible:
The shared_ptr
was introduced at the same time as rvalue-references, in C++11. The shared_ptr
already had a working reference implementation in boost
, so it could be expected to be added to standard libraries relatively quickly.
If the constructor for shared_ptr
had only supported construction from rvalue references, it would have been unusable until the compiler had also implemented support for rvalue references.
And at that time, compiler and standards development was much more asynchronous, so it could have taken years until all compiler had implemented support, if at all. (export templates were still fresh on peoples minds in 2011)
Additionally, I assume the standards committee would have felt uncomfortable standardizing an API that did not have a reference implementation, and could not even get one until after the standard was published.