STL源码阅读小记(六)——Any

前言

之前一直有听说过,不过倒是没怎么使用过,用来代替void进行类型转换,比void转换更加安全。

STL版本

本文所使用的stl版本为libc++ 13.0,属于LLVM项目。

LLVM项目Github

如果遇到不熟悉的宏定义可以参考文档Symbol Visibility Macros

Any

any是在c++17才支持的一种类型,内部可以存储任意类型,原理是利用了类型擦除,在c++常见的类型擦除有两种办法,一种是通过虚函数,如function就是利用虚函数进行类型擦除,还有一种就算利用void类型,any就是使用这种方法。

我们看一下any的定义和内部成员

class _LIBCPP_TEMPLATE_VIS any
{
    typedef __any_imp::_Action _Action;
    using _HandleFuncPtr =  void* (*)(_Action, any const *, any *, const type_info *,
      const void* __fallback_info);

    union _Storage {
        constexpr _Storage() : __ptr(nullptr) {}
        void *  __ptr;
        __any_imp::_Buffer __buf;
    };
    template <class>
    friend struct __any_imp::_SmallHandler;
    template <class>
    friend struct __any_imp::_LargeHandler;

    _HandleFuncPtr __h = nullptr;
    _Storage __s;
}

可以发现any并非是一个模板类,内部有个函数指针和存储地方,在内部对象小于3sizeof(void)时会存储在自身上,大于的话就会在堆上申请空间。any的操作基本不是由自身完成的而是通过__any_imp中的_SmallHandler和_LargeHandler操作的。

构造函数

由于构造函数有好几个,并且每个模板都好长好长,就挑一个典型的吧。

//声明
template <class _ValueType, class ..._Args,
    class _Tp = decay_t<_ValueType>,
    class = enable_if_t<
        is_constructible<_Tp, _Args...>::value &&
        is_copy_constructible<_Tp>::value
    >
  >
  _LIBCPP_INLINE_VISIBILITY
  explicit any(in_place_type_t<_ValueType>, _Args&&... __args);

//定义
template <class _ValueType, class ..._Args, class _Tp, class>
any::any(in_place_type_t<_ValueType>, _Args&&... __args) {
  __any_imp::_Handler<_Tp>::__create(*this, _VSTD::forward<_Args>(__args)...);
}

template <class _Tp>
  using _Handler = conditional_t<
    _IsSmallObject<_Tp>::value, _SmallHandler<_Tp>, _LargeHandler<_Tp>>;

可以发现声明中利用模板检测存放的类型是否有对于的构造函数和拷贝构造函数,在定义中委托给_Handler处理,而_Handler其实是一个编译期的?:三元运算,如果是小对象则_Handler为_SmallHandler,否则为_LargeHandler。

像拷贝构造函数移动构造函数基本都是如此,后续我们再看_SmallHandler和_LargeHandler究竟做了什么。

__any_imp

_SmallHandler

_SmallHandler内部是一些静态函数,比如create,copy,move等等。

我们先看看create函数

template <class ..._Args>
    _LIBCPP_INLINE_VISIBILITY
    static _Tp& __create(any & __dest, _Args&&... __args) {
        typedef allocator<_Tp> _Alloc;
        typedef allocator_traits<_Alloc> _ATraits;
        _Alloc __a;
        _Tp * __ret = static_cast<_Tp*>(static_cast<void*>(&__dest.__s.__buf));
        _ATraits::construct(__a, __ret, _VSTD::forward<_Args>(__args)...);
        __dest.__h = &_SmallHandler::__handle;
        return *__ret;
    }

可以发现any的构造实际上是在这里进行的,利用allocator在any的__s上分配空间并且构造,然后将_SmallHandler::__handle函数指针复制到any的__h上。

在_SmallHandler中还有一个__handle静态函数会被注册到any的__h上,这个函数主要作用就是根据参数传入调用不同的函数。

static void* __handle(_Action __act, any const * __this, any * __other,
                           type_info const * __info, const void* __fallback_info)
     {
        switch (__act)
        {
        case _Action::_Destroy:
          __destroy(const_cast<any &>(*__this));
          return nullptr;
        case _Action::_Copy:
            __copy(*__this, *__other);
            return nullptr;
        case _Action::_Move:
          __move(const_cast<any &>(*__this), *__other);
          return nullptr;
        case _Action::_Get:
            return __get(const_cast<any &>(*__this), __info, __fallback_info);
        case _Action::_TypeInfo:
          return __type_info();
        }
    }

_LargeHandler

_LargeHandler结构基本和_SmallHandler一样,只不过是分配空间的位置不同,就不细写了。

any_cast函数

最后就是将any转为原本类型的any_cast函数,any_cast本质上还是用来static_cast,只不过会对any中的对象进行一次判空检查,如果为空则会抛出异常。

template <class _ValueType>
inline _LIBCPP_INLINE_VISIBILITY
_LIBCPP_AVAILABILITY_THROW_BAD_ANY_CAST
_ValueType any_cast(any const & __v)
{
    using _RawValueType = __uncvref_t<_ValueType>;
    static_assert(is_constructible<_ValueType, _RawValueType const &>::value,
                  "ValueType is required to be a const lvalue reference "
                  "or a CopyConstructible type");
    auto __tmp = _VSTD::any_cast<add_const_t<_RawValueType>>(&__v);
    if (__tmp == nullptr)
        __throw_bad_any_cast();
    return static_cast<_ValueType>(*__tmp);
}

猜你喜欢

转载自blog.csdn.net/ninesnow_c/article/details/126499063