I was experimenting with shared_ptr
and make_shared
from C++11 and programmed a little toy example to see what is actually happening when calling make_shared
. As infrastructure I was using llvm/clang 3.0 along with the llvm std c++ library within XCode4.
class Object
{
public:
Object(const string& str)
{
cout << "Constructor " << str << endl;
}
Object()
{
cout << "Default constructor" << endl;
}
~Object()
{
cout << "Destructor" << endl;
}
Object(const Object& rhs)
{
cout << "Copy constructor..." << endl;
}
};
void make_shared_example()
{
cout << "Create smart_ptr using make_shared..." << endl;
auto ptr_res1 = make_shared<Object>("make_shared");
cout << "Create smart_ptr using make_shared: done." << endl;
cout << "Create smart_ptr using new..." << endl;
shared_ptr<Object> ptr_res2(new Object("new"));
cout << "Create smart_ptr using new: done." << endl;
}
Now have a look at the output, please:
Create smart_ptr using make_shared...
Constructor make_shared
Copy constructor...
Copy constructor...
Destructor
Destructor
Create smart_ptr using make_shared: done.
Create smart_ptr using new...
Constructor new
Create smart_ptr using new: done.
Destructor
Destructor
It appears that make_shared
is calling the copy constructor two times. If I allocate memory for an Object
using a regular new
this does not happen, only one Object
is constructed.
What I am wondering about is the following. I heard that make_shared
is supposed to be more efficient than using new
(1, 2). One reason is because make_shared
allocates the reference count together with the object to be managed in the same block of memory. OK, I got the point. This is of course more efficient than two separate allocation operations.
On the contrary I don't understand why this has to come with the cost of two calls to the copy constructor of Object
. Because of this I am not convinced that make_shared
is more efficient than allocation using new
in every case. Am I wrong here? Well OK, One could implement a move constructor for Object
but still I am not sure whether this this is more efficient than just allocating Object
through new
. At least not in every case. It would be true if copying Object
is less expensive than allocating memory for a reference counter. But the shared_ptr
-internal reference counter could be implemented using a couple of primitive data types, right?
Can you help and explain why make_shared
is the way to go in terms of efficiency, despite the outlined copy overhead?
As infrastructure I was using llvm/clang 3.0 along with the llvm std c++ library within XCode4.
Well that appears to be your problem. The C++11 standard states the following requirements for make_shared<T>
(and allocate_shared<T>
), in section 20.7.2.2.6:
Requires: The expression ::new (pv) T(std::forward(args)...), where pv has type void* and points to storage suitable to hold an object of type T, shall be well formed. A shall be an allocator (17.6.3.5). The copy constructor and destructor of A shall not throw exceptions.
T
is not required to be copy-constructable. Indeed, T
isn't even required to be non-placement-new constructable. It is only required to be constructable in-place. This means that the only thing that make_shared<T>
can do with T
is new
it in-place.
So the results you get are not consistent with the standard. LLVM's libc++ is broken in this regard. File a bug report.
For reference, here's what happened when I took your code into VC2010:
Create smart_ptr using make_shared...
Constructor make_shared
Create smart_ptr using make_shared: done.
Create smart_ptr using new...
Constructor new
Create smart_ptr using new: done.
Destructor
Destructor
I also ported it to Boost's original shared_ptr
and make_shared
, and I got the same thing as VC2010.
I'd suggest filing a bug report, as libc++'s behavior is broken.