C++ shared_ptr类型转换的实现原理与type traits类型萃取

思考下面这个问题:

#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,判断类型T是否能转换为类型U,enable_if_t返回一个指定的类型int,并设置默认值为0。

在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> &)
    {
    }
};

你可能感兴趣的:(c++)