`shared_ptr如何实现协方差?

问题描述 投票:4回答:2

可以从shared_ptr<Base>(即shared_ptr<Deriver>)复制或构建shared_ptr<Base> ptr = make_shared<Derived>()。但是众所周知,即使模板参数是模板类,模板类也不能相互转换。那么shared_ptrs如何检查他们的指针的值是否可以转换并进行转换呢?

c++ inheritance polymorphism shared-ptr covariance
2个回答
4
投票

是的,默认情况下,同一类模板的特化几乎没有任何关系,并且基本上被视为不相关的类型。但是,您始终可以通过定义转换构造函数(To::To(const From&))和/或转换函数(From::operator To() const)来定义类类型之间的隐式转换。

那么std::shared_ptr所做的是定义模板转换构造函数:

namespace std {
    template <class T>
    class shared_ptr {
    public:
        template <class Y>
        shared_ptr(const shared_ptr<Y>&);
        template <class Y>
        shared_ptr(shared_ptr<Y>&&);
        // ...
    };
}

虽然显示的声明允许从任何shared_ptr转换到任何其他声明,而不仅仅是模板参数类型兼容时。但标准也谈到了这些构造函数([util.smartptr]/5[util.smartptr.const]/18util.smartptr.const]/21):

出于子句[util.smartptr]的目的,当Y*可转换为T*Y*T*Y为cv U[N]时,指针类型T被称为与指针类型U[]兼容。

除非Y*T*兼容,否则[...]构造函数不得参与重载决策。

虽然这种限制可以以任何方式完成,包括编译器特定的功能,但大多数实现将使用SFINAE技术强制执行限制(替换失败不是错误)。一种可能的实现:

#include <cstddef>
#include <type_traits>

namespace std {
    template <class Y, class T>
    struct __smartptr_compatible
        : is_convertible<Y*, T*> {};

    template <class U, class V, size_t N>
    struct __smartptr_compatible<U[N], V[]>
        : bool_constant<is_same_v<remove_cv_t<U>, remove_cv_t<V>> &&
                        is_convertible_v<U*, V*>> {};

    template <class T>
    class shared_ptr {
    public:
        template <class Y, class = enable_if_t<__smartptr_compatible<Y, T>::value>>
        shared_ptr(const shared_ptr<Y>&);

        template <class Y, class = enable_if_t<__smartptr_compatible<Y, T>::value>>
        shared_ptr(shared_ptr<Y>&&);

        // ...
    };
}

这里辅助模板__smartptr_compatible<Y, T>充当“特征”:它有一个static constexpr成员value,当类型与定义兼容时为true,否则为false。然后std::enable_if是一个特征,当它的第一个模板参数是type时有一个名为true的成员类型,或者当它的第一个模板参数是type时没有名为false的成员,使得类型别名std::enable_if_t无效。

因此,如果构造函数的模板类型推导推导出类型Y以使Y*T*不兼容,则将Y替换为enable_if_t默认模板参数是无效的。由于在替换推导出的模板参数时会发生这种情况,因此效果只是从重载决策的考虑中删除整个函数模板。有时,SFINAE技术用于强制选择不同的重载,或者在此处(大多数情况下),它只会使用户的代码无法编译。虽然在编译错误的情况下,它将有助于在输出中某处出现模板无效的消息,而不是内部模板代码中更深层的错误。 (另外,像这样的SFINAE设置使得不同的模板可以使用自己的SFINAE技术来测试某个模板特化,类型相关表达式等是否有效。)


-1
投票

它的工作原理是因为shared_ptr具有(以及其他)模板化构造函数

template<typename U> shared_ptr(U * ptr);

如果U *不能转换为shared_ptr包含的类型,那么您将在shared_ptr实现中的某处隐藏错误。

© www.soinside.com 2019 - 2024. All rights reserved.