To understand this problem let's first consider what would be the result of calling std::make_shared<class_type>()
,
It returns temporary object which means Xvalue
an eXpiring
value whose resources can be reused. Now let's see both cases,
some_vector.push_back(make_shared<ClassName>());
std::vector
have two overload of push_back
and one of them accept rvalue
reference that is
constexpr void push_back( T&& value );
It means value
is moved into new element, but how? rvalue
overload of push_back
will move construct new value by invoking shared_ptr( shared_ptr&& r ) noexcept;
and ownership of r
will be taken and r
become empty.
some_vector.emplace_back(make_shared<ClassName>());
In emplace_back( Args&&... args )
element is constructed through std::allocator_traits::construct
by perfect forwarding args..
through std::forward<Args>(args)...
, It means rvalue
will perfect forward and cause same move constructor shared_ptr( shared_ptr&& r ) noexcept;
to be invoked.
Conclusion is, both push_back
and emplace_back
have same effect.
But what is explained above doesn't happen because compiler
comes into the picture and what it does, it perform optimization, It means rather than creating temporary objects and moving them into other objects, it directly creates objects in place.
Again result is same in both cases.
Below, supporting code for compiler optimization theory is included and as you can see output only prints one constructor call.
#include <iostream>
using std::cout;
using std::endl;
class Object{
public:
explicit Object(int );
Object(const Object& );
Object(Object&& );
};
Object::Object(int ){
cout<< __PRETTY_FUNCTION__<< endl;
}
Object::Object(const Object& ){
cout<< __PRETTY_FUNCTION__<< endl;
}
Object::Object(Object&& ){
cout<< __PRETTY_FUNCTION__<< endl;
}
int main(){
[[maybe_unused]] Object obj(Object(1));
}
Output:
Object::Object(int)