思考下面这个问题:
#include
#include
using namespace std;
class A{
public:
int data;
};
class B : public A{
};
template <typename _Ty>
class TypeCast{
};
int main()
{
shared_ptr<B> b = make_shared<B>();
shared_ptr<A> a = b; // 正确,为何能将b转成a?
TypeCast<B> tb;
TypeCast<A> ta = tb; // 错误
return 0;
}
上面的代码,很容易理解,B虽然是A的子类,但是TypeCast和TypeCast并无关系,不能隐式转换能理解。那shared_ptr是如何实现的呢?
很容易想到,此隐式类型转换是通过构造函数实现的。
修改一下TypeCast,也能实现和shared_ptr一样的隐式类型转换:
template <typename _Ty>
class TypeCast{
public:
TypeCast() = default;
template<typename _Ty2>
TypeCast(const TypeCast<_Ty2>& ){
}
};
是可以实现了,但是又出现一个新问题,任何一个TypeCast模板对象都能使用此构造函数,即使_Ty2不是Ty的子类也能调用此构造函数实现隐式转换。
TypeCast<A> ta = tb; // 正确
class C{};
TypeCast<C> tc = ta; // 正确
如何限定必须要有父子关系的_Ty才能调用此构造函数?
使用type_traits类型萃取。
通过type_traits可以实现在编译期计算、查询、判断、转换和选择,增强了泛型编程的能力。
template <typename _Ty>
class TypeCast{
public:
TypeCast() = default;
// 编译器 vc++ 2019,gcc的实现不同
template <class _Ty2, enable_if_t<_SP_pointer_compatible<_Ty2, _Ty>::value, int> = 0>
TypeCast(const TypeCast<_Ty2>& ){
}
};
TypeCast<A> ta = tb; // 正确,TypeCast ta = TypeCase::TypeCase(tb);
class C{};
TypeCast<C> tc = ta; // 错误
类型萃取是如何实现的?
using _my_type = enable_if_t<_SP_pointer_compatible<B, A>::value, int>;
// _my_type 是 int
using _my_type2 = enable_if_t<_SP_pointer_compatible<B, C>::value, int>;
// _my_type2 是 error type
通过type_traits的is_convertible
在msvc和gcc中的shared_ptr构造函数模板是不同的。
// msvc,直接通过判断类型是否能转换来推断是否能调用此构造函数
// 若构造成功,模板定义:template;构造失败则无类型:template
template <class _Ty2, enable_if_t<_SP_pointer_compatible<_Ty2, _Ty>::value, int> = 0>
shared_ptr(const shared_ptr<_Ty2>& _Other) noexcept {
// construct shared_ptr object that owns same resource as _Other
this->_Copy_construct_from(_Other);
}
// gcc,通过判断父类__shared_ptr是否有指定参数的构造函数来推断是否可调用此构造函数,
// 若能构造成功,模板定义:template,若不能构造则类型错误:template
template<typename _Yp, typename = _Constructible<const shared_ptr<_Yp>&>>
shared_ptr(const shared_ptr<_Yp>& __r) noexcept
: __shared_ptr<_Tp>(__r) { }
// __shared_ptr的此构造函数判断类型是否能转换来推断是否能调用此构造函数
template<typename _Yp, typename = _Compatible<_Yp>>
__shared_ptr(const __shared_ptr<_Yp, _Lp>& __r) noexcept
: _M_ptr(__r._M_ptr), _M_refcount(__r._M_refcount)
{ }
// gcc 和 msvc 的实现
#if _WIN32
template <class _Yty, class _Ty>
using _sp_convertible = _SP_pointer_compatible<_Yty, _Ty>;
#else
template <typename _Yp_ptr, typename _Tp_ptr>
using _sp_convertible = __sp_compatible_with<_Yp_ptr*, _Tp_ptr*>;
#endif
template <typename _Ty>
class TypeCast
{
public:
TypeCast() = default;
template <class _Ty2, enable_if_t<_sp_convertible<_Ty2, _Ty>::value, int> = 0>
TypeCast(const TypeCast<_Ty2> &)
{
}
};