Consider the following example:
#include <memory>
template<class T>
class RefSP {
private:
std::shared_ptr<T> p;
public:
template<class A>
RefSP(A&& v) : p(std::forward<A>(p)) {}
};
template <
class T,
class U,
typename std::enable_if<std::is_base_of<std::shared_ptr<U>, T>::value>::type* = nullptr>
inline RefSP<U>*
make_ref_sp(T&& p)
{
return new RefSP<U>(std::forward<T>(p));
}
int main()
{
auto sp = std::make_shared<int>(42);
auto x = make_ref_sp(sp);
}
I get compilation error
In function 'int main()':
25:28: error: no matching function for call to 'make_ref_sp(std::shared_ptr<int>&)'
25:28: note: candidate is:
17:1: note: template<class T, class U, typename std::enable_if<std::is_base_of<std::shared_ptr<_Tp2>, T>::value>::type* <anonymous> > RefSP<U> make_ref_sp(T&&)
17:1: note: template argument deduction/substitution failed:
25:28: note: couldn't deduce template parameter 'U'
The question is, how can I fix the code so that U
will be inferred given that T
is std::shared_ptr<U>
while preserving move-semantics.
std::is_base_of
is a trait for inheritance...
You would need special trait for that:
template <typename> struct is_shared_ptr : std::false_type {};
template <typename T> struct is_shared_ptr<std::shared_ptr<T>> : std::true_type
{
// Possibly add information here
// using element_type = T;
};
template <
class T,
typename std::enable_if<is_shared_ptr<typename std::decay<T>::type>::value, int>::type = 0>
RefSP<typename std::decay<T>::type::element_type>
make_ref_sp(T&& p)
{
return RefSP<U>(std::forward<T>(p));
}
I would simply do:
#include <memory>
template <class T>
class RefSP {
private:
std::shared_ptr<T> p;
public:
explicit RefSP(std::shared_ptr<T>&& v) : p(std::move(v)) {}
};
template <class T>
RefSP<T> make_ref_sp(std::shared_ptr<T>&& p)
{
return RefSP<T>(std::move(p));
}
int main()
{
auto sp = std::make_shared<int>(42);
auto x = make_ref_sp(std::move(sp));
}
If you want to allow implicit copy, you might add corresponding overloads:
template <class T>
RefSP<T>::RefSP(const std::shared_ptr<T>& v) : p(v) {}
template <class T>
RefSP<T> make_ref_sp(const std::shared_ptr<T>& p)
{
return RefSP<T>(p);
}
And if you don't want to write overload and let user choose between (implicit) copy and move, replace all overloads by:
template <class T>
RefSP<T>::RefSP(std::shared_ptr<T> v) : p(std::move(v)) {}
template <class T>
RefSP<T> make_ref_sp(std::shared_ptr<T> p)
{
return RefSP<T>(std::move(p));
}