Rationale on explicit shared_ptr constructor
Hi, (I can't beleive this wouldn't have been discussed already, but I haven't been able to find an answer in my searches [Google's search results into boost's web maillist seem to be incoherent.].) I recently converted a portion of a medium-sized project from loki::Smart_Ptr to boost::shared_ptr (primarily for weak_ptr support), and almost everything worked out of the box, except: boost: template<class Y> explicit shared_ptr <#constructors>(Y * p); loki: SmartPtr(const StoredType& p) In other words, code like this stopped working: some_smart_ptr<int> my_function() { return new int; // error: conversion from `int*' to non-scalar type `boost::shared_ptr<int>' // requested } Even when I adopt the convention to convert the return value to the smart pointer type such as with "return some_smart_ptr<int>(new int)", returning a null pointer as 'zero' doesn't work: some_smart_ptr<int> my_function() { return some_smart_ptr<int>(0); // error: no matching function for call to // `boost::shared_ptr<int>::shared_ptr(int)' } I've worked around this by simply using "return some_smart_ptr<int>();". However, I feel somewhat strongly that this sort of usage is unnecessarily ugly, and confuses the meaning of the code unnecessarily. More importantly, it causes smart pointers to behave differently from ordinary pointers, which is undesirable, as it requires the user (who may not know or care anything about the smart pointer, or even know that it is one, depending on a module's implementation and documentation) to have to learn and use and debug additional semantics. So my question follows. I understand that there are very good reasons for disallowing automatic conversion from a smart pointer, but why disallow automatic conversion to the smart pointer type also? If there are strong reasons for this, would it be reasonable to document this somewhere in the shared_ptr documentation? If not, is this particular aspect of shared_ptr possibly subject to discussion or change in the future? Thank you for your time, Aaron W. LaFramboise
"Aaron" == Aaron W LaFramboise
writes:
Aaron> I recently converted a portion of a medium-sized project from Aaron> loki::Smart_Ptr to boost::shared_ptr (primarily for weak_ptr support), Aaron> and almost everything worked out of the box, except: Aaron> boost: template<class Y> explicit shared_ptr <#constructors>(Y * p); Aaron> loki: SmartPtr(const StoredType& p) Aaron> In other words, code like this stopped working: Aaron> some_smart_ptr<int> my_function() { Aaron> return new int; // error: conversion from `int*' to non-scalar type Aaron> `boost::shared_ptr<int>' Aaron> // requested Aaron> } Aaron> Even when I adopt the convention to convert the return value to the Aaron> smart pointer type such as with "return some_smart_ptr<int>(new int)", Aaron> returning a null pointer as 'zero' doesn't work: Aaron> some_smart_ptr<int> my_function() { Aaron> return some_smart_ptr<int>(0); // error: no matching function for Aaron> call to Aaron> // `boost::shared_ptr<int>::shared_ptr(int)' Aaron> } Aaron> I've worked around this by simply using "return some_smart_ptr<int>();". Aaron> However, I feel somewhat strongly that this sort of usage is Aaron> unnecessarily ugly, and confuses the meaning of the code unnecessarily. Aaron> More importantly, it causes smart pointers to behave differently from Aaron> ordinary pointers, which is undesirable, as it requires the user (who Aaron> may not know or care anything about the smart pointer, or even know that Aaron> it is one, depending on a module's implementation and documentation) to Aaron> have to learn and use and debug additional semantics. Aaron> So my question follows. I understand that there are very good reasons Aaron> for disallowing automatic conversion from a smart pointer, but why Aaron> disallow automatic conversion to the smart pointer type also? If there Aaron> are strong reasons for this, would it be reasonable to document this Aaron> somewhere in the shared_ptr documentation? If not, is this particular Aaron> aspect of shared_ptr possibly subject to discussion or change in the future? This is intentional. Consider the following code; using_func() calls func() with a plain int*, and the int that the pointer points to is on the stack. void func(int* p) { // whatever } void using_func() { int i; func(&i); } Now consider somebody changes func() to take a shared_ptr<> instead of the plain int*. void func(shared_ptr<int> p) { // whatever } If automatic conversion was allowed, the code would still compile. The compiler would generate a temporary shared_ptr<int> before calling func(). In other words, void using_func() { int i; func(&i); } would actually be void using_func() { int i; int* ip = &i; shared_ptr<int> temporary(ip); func(temporary); } Now this is fatal because temporary's destructor is run at the end of using_func(). The destructor will see that it is the last instance that references the pointer, and will delete the int instance - which is on the stack. Without automatic conversion, the compiler would barf and using_func() would have to be fixed. Joerg
participants (2)
-
Aaron W. LaFramboise
-
Joerg Faschingbauer