7

I am working with a code base which makes extensive use of the following syntax:

shared_ptr<Object> myObject = (shared_ptr<Object>) new Object();

I noticed that I cannot access private constructors with make_shared, but shared_ptr<Object> myObject = (shared_ptr<Object>) new Object(); works just fine. Should I be using it just because it seems to work? Are there any dangers? How is it different than make_shared?

I am aware of the answer in this question, which makes comparisons between make_shared and:

std::shared_ptr<Object> p2(new Object("foo"));

but I haven't been able to find a reference to the syntax I came across. Is it different, or is it the same as the above?

Nikos Kazazakis
  • 669
  • 5
  • 18

1 Answers1

10

make_shared allocates the object in the same block of memory as the control block. This improves cache coherency and cuts out an allocation.

One way to permit make_shared to do this is to have a private token within your class, and a public constructor that consumes that token.

class Object {
private:
  struct token_t{ private: token_t() {}; friend class Object; };
  static token_t token() { return {}; }
  Object() = default;
public:
  Object( token_t ):Object() {}
};

now we can make_shared<Object>( Object::token() ).

This gives us one allocation, and doesn't violate the privacy of the construction as only things with access to private fields of Object can call that constructor. They can, however, pass the token to another function (like make_shared) and it in turn can call the constructor in question. This works with more arguments, naturally.

As for your syntax:

std::shared_ptr<Object> myObject = (std::shared_ptr<Object>) new Object();

(std::shared_ptr<Object>) new Object(); simply explicitly constructs a shared_ptr<Object> from new Object(). It is equivalent to std::shared_ptr<Object>(new Object).

We then take this prvalue and construct myObject from it. In C++03 11 and 14, this copy/move construction is elided. In C++17, the prvalue "construction instructions" are directly applied to myObject. In practice, this results in the same machine code (unless you are a fool and explicitly tell your compiler to not elide construction).

In short, it works. The only downside is the double-allocation of the separate control-block from the Object allocation.

Alexis Wilke
  • 15,168
  • 8
  • 60
  • 116
Yakk - Adam Nevraumont
  • 235,777
  • 25
  • 285
  • 465
  • I get an error... The way it is the `token()` function is private... so it cannot be used in the `make_shared<>()` call. – Alexis Wilke Jun 13 '17 at 23:12
  • 2
    @alexis the point is that make shared can only be called with a token in a context that you have access to private data from the class (or where you are granted oermission by proxy by them passing you a token from such a context). Simply make the constructor public if you do not want access control on it at all. Ie suppose `static std::shared_ptr Class::make(){ return std::make_shared( token() ); }` – Yakk - Adam Nevraumont Jun 13 '17 at 23:49