Reputation: 5413
1)
template<typename T, typename Arg>
shared_ptr<T> factory(Arg arg)
{
return shared_ptr<T>(new T(arg));
}
2)
template<typename T, typename Arg>
shared_ptr<T> factory(Arg& arg)
{
return shared_ptr<T>(new T(arg));
}
3)
template<typename T, typename Arg>
shared_ptr<T> factory(Arg const & arg)
{
return shared_ptr<T>(new T(arg));
}
*)Why is number 3 is preferred than number 1 and number 2?
*)if factory(41) is called, why is called on rvalue?
*)# define BOOST_ASIO_MOVE_ARG(type) type&&. what's && in this case?
Upvotes: 4
Views: 4611
Reputation: 9113
Actually, approach #3 is not better than 1 and 2. It all depends on T
's constructor(s).
A somewhat standard way to do this is using rvalue references, which are the &&
you mention.
So, a better factory would be something like this: (which is what perfect forwarding actually is)
template<typename T, typename Arg>
std::shared_ptr<T> factory(Arg && arg)
{
return std::shared_ptr<T>(new T (std::forward<Arg>(arg)));
}
Due to reference collapsing rules of C++, this function can take (and forward) both rvalue refences and lvalue references, i.e., if you pass it an rvalue, it will forward that rvalue faithfully to T
's constructor, and if you give it an lvalue, the two kinds of reference (Something & && arg
) will collapse and the lvalue reference will be forwarded to the underlying constructor.
I think I covered your questions here, but to answer explicitly,
const&
and &
overloads of the factory
function. The const&
version is nicer and it will actually accept temporary values, but if your underlying T
type has a constructor that accepts a non-const ref, then you would get a compile error because a const&
is not implicitly casted to a &
.41
(this is not 100% technically correct, but I think it's OK to think of lvalues and rvalues this way.)rvalue reference
. You need to read about that; an explanation will not fit here, and there are already several great ones just a Google search away!UPDATE: As mentioned tangentially in the comments, using make_shared
can be better than constructing a shared_ptr
with a just-new
ed pointer. make_shared
might achieve better efficiency by allocating the control block (which includes the reference count) along with the object itself, which would provide better cache locality when accessing the reference counter and the object, and also may save one memory allocation. Even if the implementation has none of the optimizations, it won't be any worse than the above version. So use std::make_shared
whenever possible! And this would be how you do it here:
template<typename T, typename Arg>
std::shared_ptr<T> factory (Arg && arg)
{
return std::make_shared<T>(std::forward<Arg>(arg));
}
Upvotes: 11
Reputation: 52471
*)Why is number 3 is preferred than number 1 and number 2?
(1) won't work if Arg
is non-copyable.
(2) won't allow you to pass an rvalue, as in factory<int>(42);
Note that none of the three examples involve perfect forwarding. I'm not sure what the subject of your question refers to.
*)if factory(41) is called, why is called on rvalue?
I'm not sure I understand the question. 41
is an rvalue by definition.
*)# define BOOST_ASIO_MOVE_ARG(type) type&&. what's && in this case?
type&&
is an rvalue reference to type
.
Upvotes: 1