Obtaining a ‘shared_ptr’ from ‘this’ is possible using the ‘enable_shared_from_this’ class. It’s a feature that allows a class to reference itself within a smart pointer based API. Beware an ugly implementation detail that renders their natural use somewhat unsafe: the ‘shared_ptr’ itself is not automatically created. Calling ‘shared_from_this’ on a non-shared object results in undefined behaviour.
void register_service( shared_ptr<service> ); class service : public enable_shared_from_this<service> { public: void register() { register_service( shared_from_this() ); } } //intended use auto good = make_shared<service>(); good->register(); //bad use service bad; bad.register(); //undefined behaviour on 'shared_from_this' //also bad auto pbad = new service(); pbad->register();
‘enable_shared_from_this’ requires the object be owned by a ‘shared_ptr’, either via a call to ‘make_shared’ or a ‘shared_ptr’ constructor. This requirement is unfortunately never checked: if the object is not owned by a ‘shared_ptr’ no compile-time, nor run-time error will be generated. The code ventures into the land of undefined behaviour.
Privatize the constructors
Blocking invalid creation is achieved by making the class constructors private. This prevents any direct instantiation. Instead a new ‘create’ method does the construction on our behalf, returning an appropriate ‘shared_ptr’.
class service : public enable_shared_from_this<service> { service() = default; public: static shared_ptr<service> create() { return shared_ptr<service>( new service ); } }; //only available use (and it's valid) auto good = service::create(); //compile-time failures auto fail = new service(); service mfail;
The example works, but not all possible constructors are covered. At a minimum the copy constructor must also be made private. Typically a user defined constructor will also be needed. It would be unfortunate if every constructor needed a matching ‘create’ method. Prior to C++11 that was the case, but now we can use variadic templates and parameter forwarding.
class service : public enable_shared_from_this<service> { service() = default; service( const service & o ) = default; // or = delete if you want service( config_settings c ); public: template<typename ... T> static shared_ptr<service> create( T&& ... all ) { return shared_ptr<service>( new service( std::forward<T>(all)... ) ); } };
This ‘create’ method takes any number of parameters and passes them to one of the private constructors. The ‘…’ is part of the variadic template syntax. The ‘&&’ and ‘std::forward’ are part of the universal reference and perfect forwarding system. It’s definitely worth the time to learn those C++ features.
make_shared
It might be tempting to use ‘make_shared’ as is common when creating shared objects. It cannot be used here due to visibility rules. Since ‘make_shared’ is not a member function of ‘service’ it doesn’t have access to the private constructors. I don’t suspect much is lost; the purpose of ‘make_shared’ is a small memory optimization. I would hope that marking a class ‘enable_shared_from_this’ is also enough to get the same optimization. It is almost certainly not a relevant detail in most cases where ‘enable_shared_from_this’ is used.
Visibility rules do however create another issue: a generic template that implements this ‘create’ function can’t be built. It must be copied into each class that needs it; a base class is unable to access the private constructors needed to implement ‘create’. To remove the duplication a macro will be required.
Categories: Defective Language, Programming
I tried making make_shared a friend but it wouldn’t give in; GCC 4.8.
Any idea why? So far I like the idea of a shared_ptr only implementable class, it would guarantee safety.
make_shared can’t be used if the class constructor is private.
The problem is not knowing what functions make_shared actually calls. If it called ‘new’ directly a friend function would work. It could however call ‘new’ in some sub-function it uses, in which case your friend declaration will do nothing.
There is of courses a tagging technique, where you append a private structure to each constructor and also pass it to make_shared. This works, but has the drawback you can’t use any default constructors.
Two years later..
I also wanted to be able to use make_shared. I found a hack that seems to work, but it introduces an extra layer of indirection and some duplication of code. Anyway, I think should be possible to expand this example to cover all sorts of constructors as well.
Why the `static_pointer_cast` in `create`, it shouldn’t be necessary?
You can use the `Element(Element &other) = delete;` syntax to delete a standard constructor.
But I think my code should have covered your example as well, or does it not covere the zero parameters case? I used variadic templates to try and cover all cases.
I think the cast is necessary because of the way I use the ‘ElementConstructor’ as template argument to make_shared. This makes make_shared return a shared_ptr. Since we want the ‘create’-method to return a shared_ptr, and since there are no implicit conversion, we need to do the cast.
Yeah, surely I could use the ‘Element(Element &other) = delete;’ syntax :) I’m just kind of more used to declare it privately, since in effect I guess it does the same, but it’s also legal in c++99.
Your code is more general in terms of numbers of parameters that can be passed to create :) It does take the zero-parameters case as well. After posting my code to you, I tried to change my code to be as general as yours, with your code as example, and it worked :)
The absolutely only thing I wanted to demonstrate was that it Is possible to figure a way where Elements constructor is private, and also use make_shared in the ‘create’-method, as opposed to using shared_ptr(new Element()). Now it’s okay to question how interesting this actually is, though. make_shared do have some benefits to it, but my code doesn’t exactly adhere so much to the ‘kiss’-principle as your code does.
using make_shared