On 10/9/2013 3:26 AM, Quoth Thorsten Ottosen:
Another benefit of this approach is that we can create different behaviors without relying on the absurd subtleties of the preprocessor:
typedef boost::shared_ptr<T> SharedT; // normal shared_ptr typedef boost::shared_ptr
NonNullSharedT; // no null enforced by assertions typedef boost::shared_ptr NeverNullSharedT; // no null enfoced by exceptions
throw_on_null might be better for the latter, just to be explicit, although it does break the "non_null" naming similarity.
The shared_ptr implementation would have to check its template argument and emit special code in a few places. This is very easy to do, and far easier than specializing/wrapping the class.
Ok, I was assuming something that did not modify the existing shared_ptr implementation. If you allow that then things get much tidier.
I suppose there is still a need to break cycles. Why would this have changed.
But part of the semantics of weak_ptr is that it can point to a non-existent (expired) object, which is effectively the same as a null one. And lock() must be able to return an empty pointer, so it would have to be a regular shared_ptr<T> rather than a shared_ptr<*_non_null<T>>. So I'm not sure if a weak_ptr<*_non_null<T>> could make sense. Also, as someone pointed out earlier (apologies, I've lost track of the original post), the shared_ptr<T>(weak_ptr<T>) constructor already throws if the weak_ptr is empty or expired. (There's a nothrow variant of the constructor but as that is undocumented and requires using a detail class I think that's not intended for public use.) Although, just to be confusing (it's documented behaviour though), this code will throw: boost::shared_ptr<int> s1; boost::weak_ptr<int> w(s1); boost::shared_ptr<int> s2(w); // throws bad_weak_ptr And this code will not: boost::shared_ptr<int> s1((int*)0); boost::weak_ptr<int> w(s1); boost::shared_ptr<int> s2(w); // doesn't throw; s2 is false