std::shared_ptr<T> implicit conversion from T*

This is a discussion on std::shared_ptr<T> implicit conversion from T* within the C++ Programming forums, part of the General Programming Boards category; can anyone explain to me the rationale for why the constructor of std::shared_ptr that takes a raw pointer as its ...

std::shared_ptr<T> implicit conversion from T*

can anyone explain to me the rationale for why the constructor of std::shared_ptr that takes a raw pointer as its parameter is marked explicit? it seems much more logical to allow implicit conversion like so:

Code:

std::shared_ptr<T> myPtr = new T();

additionally, there is no assignment operator that accepts a raw pointer.

I don't understand the reasons behind this, as it makes the code somewhat harder to follow, and less natural and convenient to write.

I think the reasoning behind explicit constructors is explained in Effective C++ by Scott Meyer. It has to do with preventing implicit conversions. In this case, it is not necessarily proper for you to call a function that expects a shared_ptr type, and you pass in a raw pointer type.

Code:

void foobar(std::shared_ptr<T>& p);
T* my_p = new T;
foobar(my_p);

Having the std::shared_ptr class's single argument constructor marked explicit helps prevent code like this from working. If you intended to pass in your raw pointer, you have to call the constructor, using constructor syntax, first.

that code fails because the compiler will complain about initializing a non const reference with an rvalue, but I get where you're going with that. I guess it makes some sense, but it slows me down when I have to use constructor syntax, because it's just less natural. I suspect that others may share my frustration. maybe I'll attend the standards committee meeting in september and bring it up

@Elkvis: One reason is the way ambiguity travels uphill with overloads (Example #1). This is just the simplest example I could think of out of hand. The committee isn't likely to consider a change because the same problems come around with nested functions as well.

@whiteflags: There are two things at play (Example #2): constant "lvalue" temporaries and implicit versus explicit constructors. I feel that you are getting the two confused.

you speak of ambiguity, but I guess I don't understand what's ambiguous about this:

Code:

std::shared_ptr<std::string> stringPtr = new std::string("foo");

obviously this won't compile, because the constructor is marked explicit, but that's what doesn't make sense to me. it seems perfectly logical for it to work this way, and it makes code flow a little more naturally. can you think of a situation where this sort of thing would be inappropriate and/or dangerous with smart pointers?

of course not, but the examples did little to help me understand why they should answer my question.

Originally Posted by phantomotap

ambiguity travels where a template implicit constructor goes.

that's the part I don't understand. you'll probably have to explain it in simpler terms. how does ambiguity travel? a shared_ptr has one template argument, and I guess I fail to see why implicitly constructing a shared_ptr from a raw pointer is any different from implicitly constructing a std::string from a const char pointer. if the template parameter is a well defined type, there doesn't seem to be any room for ambiguity.

An implicit constructor is considered as part of overload resolution when the type considered is used an argument.

Play with the code because I don't think I can explain it in simpler terms.

a shared_ptr has one template argument, and I guess I fail to see why implicitly constructing a shared_ptr from a raw pointer is any different from implicitly constructing a std::string from a const char pointer.

The template parameter associated with the `smart_ptr' is irrelevant.

[Edit]
Actually, I edited the example; as you can see, `STest' itself being a template isn't really the issue.
[/Edit]

Once again, look at the example. The constructor is a template.

The ambiguity I reference comes from how implicit conversions are considered during overload resolution; the template constructor allows implicit conversion from any pointer.

if the template parameter is a well defined type, there doesn't seem to be any room for ambiguity.

Well, a template argument is not "well-defined" by definition.

The `std::smart_ptr' constructor is the same, except for `explicit', as my example for you.

The resolution is ambiguous because any pointer, even ones completely unrelated to the type desired by the `std::smart_ptr', will be considered during overload resolution.

ok, that makes a little more sense, now. I didn't realize that the constructor in question was a template function as well. what problem does it solve for the constructor to be a template function, and accept any pointer as a parameter? derived objects implicitly convert to their base type, so it seems unlikely that it's intended to assist with polymorphic types.

what problem does it solve for the constructor to be a template function, and accept any pointer as a parameter?

O_o

Awesomeness.

No, really, the point is to provide an incredible level of functionality. There are actually a lot of benefits related to knowing the real type of the resource during construction instead of whatever base is relevant to the interface.

I'll give you a couple of examples:

1): The "type erasure" technique employed allows the "deleter"--the optional function called to destroy the owned resource--to be type-safe without being an actual part of the type of the `smart_ptr'. That allows me to use `FruityDelete' and you to use `DeerDelete' with the same `std::smart_ptr<Object>' type in the interfaces.

2): It facilitates the use of "non-polymorphic base class" aspect of the "non-virtual interface pattern" strategy. The constructor creates a type-safe object that knows the real type of the resource provided so that the "deleter"--one is created if no provided--can destroy the resource honoring the real type which allows us to have a non-virtual destructor.

[Edit]
By the way, these benefits directly related to parametric polymorphisms.

So, yes, it is intended to assist with polymorphic code, just not inheritance based polymorphisms.

As a further example, let us pretend our particular flavor of "non-virtual interface pattern" requires that object do some sort of subscribing or unsubscribing from an external resource. (You may pretend a shared library is being managed by reference count.) This strategy allows us to code a non-member, non-friend--completely normal--function depending on overload resolution for the specific derived types so that we may assume a `Unsubscribe' API exists with exactly that name. The type-safe "type erasure" employed will call the correct overload during destruction because the exact type of the resource is known.
[/Edit]

Explicit constructors are used in reusable libraries, like the C++ standard library, when there is at least one circumstance where it is a really bad idea for implicit construction to occur.

whiteflags gave one example where an implicit conversion of a pointer to a shared_pointer is a bad idea (as, in that example, the object just allocated will be quietly deleted by the function call). There are other examples.

Given a choice between a little inconvenience (having to initialise without using assignment syntax, because the constructor is explicit) and having a major inconvenience (a bug that is hard to find because an implicit conversion is permitted in circumstances where something bad can happen) I will choose the small inconvenience.

In other words, I agree with the specification of shared_ptr in this case.

If I seem grumpy or unhelpful in reply to you, or tell you you need to demonstrate more effort before you can expect help, it is likely you deserve it. Suck it up, Buttercup, and read this, this, and this before posting again.

thanks for the great explanation, soma. I kind of understand now. I never really thought of using a smart pointer to manage anything other than automatic memory management for a normal pointer to an object. that concept is well beyond the scope of anything I do in the code that I write, so it may just be more advantageous for me to write my own much more basic shared pointer class for my purposes.

This isn't something that is costing you a lot in terms of performance or memory use.

but it has a significant impact, to me at least, on code readability. readability is more important to me than performance or memory use, especially since 99% or more of the processing in the programs I typically write is done by a database engine (MySQL), outside of my code.