• std::variant 源码分析


    std::variant 源码分析

    背景:

    c++17 里边有个std::variant 它的用法挺神奇的,类似c 语言中的union ,本文将从源码角度,看下支撑它的到底是什么

    std::variant 用法

    C++
    #include
    #include
    #include
    #include
     
    int main()
    {
        std::variant v, w;
        v = 42; // v contains int
        int i = std::get(v);
        assert(42 == i); // succeeds
        w = std::get(v);
        w = std::get<0>(v); // same effect as the previous line
        w = v; // same effect as the previous line
     
    //  std::get(v); // error: no double in [int, float]
    //  std::get<3>(v);      // error: valid index values are 0 and 1
     
        try {
            std::get(w); // w contains int, not float: will throw
        }
        catch (const std::bad_variant_access& ex) {
            std::cout << ex.what() << '\n';
        }
     
        using namespace std::literals;
     
        std::variant x("abc");
        // converting constructors work when unambiguous
        x = "def"; // converting assignment also works when unambiguous
     
        std::variant y("abc");
        // casts to void const * when passed a char const *
        assert(std::holds_alternative(y)); // succeeds
        y = "xyz"s;
        assert(std::holds_alternative(y)); // succeeds
    }

    Output:

    C++
    std::get: wrong index for variant

    std::variant 源码分析

    C++
    template
    class _LIBCPP_TEMPLATE_VIS variant;

    template
    class _LIBCPP_TEMPLATE_VIS variant
        : private __sfinae_ctor_base<
              __all...>::value,
              __all...>::value>,
          private __sfinae_assign_base<
              __all<(is_copy_constructible_v<_Types> &&
                     is_copy_assignable_v<_Types>)...>::value,
              __all<(is_move_constructible_v<_Types> &&
                     is_move_assignable_v<_Types>)...>::value>
     {
         ......
     private:
      __variant_detail::__impl<_Types...> __impl;
     }

    这个里边源码极其复杂,这里我们只分析重点, 可以看出来variant内部有个private的成员, __impl,也就搞清楚impl的结构,就搞清楚了整个variant的底层支撑原理

    __impl

    C++
    template
    class _LIBCPP_TEMPLATE_VIS __impl
        : public __copy_assignment<__traits<_Types...>> {
      using __base_type = __copy_assignment<__traits<_Types...>>;
    {
        .....
    };

    接下来看下__copy_assignment的定义

    __copy_assignment

    C++
    template
    class _LIBCPP_TEMPLATE_VIS __copy_assignment;

      template                                                    \
      class _LIBCPP_TEMPLATE_VIS __copy_assignment<__traits<_Types...>,           \
                                                    copy_assignable_trait>         \
          : public __move_assignment<__traits<_Types...>> {                        \
        using __base_type = __move_assignment<__traits<_Types...>>;                \
                                                                                   \
      public:                                                                      \
        using __base_type::__base_type;                                            \
        using __base_type::operator=;                                              \
                                                                                   \
        __copy_assignment(const __copy_assignment&) = default;                     \
        __copy_assignment(__copy_assignment&&) = default;                          \
        ~__copy_assignment() = default;                                            \
        copy_assignment                                                            \
        __copy_assignment& operator=(__copy_assignment&&) = default;               \
      }

    __copy_assignment 又继承了__move_assignment

    __move_assignment

    C++
    template
    class _LIBCPP_TEMPLATE_VIS __move_assignment;
                      
      template                                                    \
      class _LIBCPP_TEMPLATE_VIS __move_assignment<__traits<_Types...>,           \
                                                    move_assignable_trait>         \
          : public __assignment<__traits<_Types...>> {                             \
        using __base_type = __assignment<__traits<_Types...>>;                     \
                                                                                   \
      public:                                                                      \
        using __base_type::__base_type;                                            \
        using __base_type::operator=;                                              \
                                                                                   \
        __move_assignment(const __move_assignment&) = default;                     \
        __move_assignment(__move_assignment&&) = default;                          \
        ~__move_assignment() = default;                                            \
        __move_assignment& operator=(const __move_assignment&) = default;          \
        move_assignment                                                            \
      }

    __move_assignment 又继承了__assignment

    __assignment

    C++
    template
    class _LIBCPP_TEMPLATE_VIS __assignment : public __copy_constructor<_Traits> {
      using __base_type = __copy_constructor<_Traits>;

    public:
      using __base_type::__base_type;
      using __base_type::operator=;

      template _Ip, class... _Args>
      inline _LIBCPP_INLINE_VISIBILITY
      auto& __emplace(_Args&&... __args) {
        this->__destroy();
        auto& __res = this->__construct_alt(__access::__base::__get_alt<_Ip>(*this),
                              _VSTD::forward<_Args>(__args)...);
        this->__index = _Ip;
        return __res;
      }
      .......
      }

    __assignment 又继承了__copy_constructor

    __copy_constructor

    C++
    emplate
    class _LIBCPP_TEMPLATE_VIS __copy_constructor;

      template                                                    \
      class _LIBCPP_TEMPLATE_VIS __copy_constructor<__traits<_Types...>,          \
                                                     copy_constructible_trait>     \
          : public __move_constructor<__traits<_Types...>> {                       \
        using __base_type = __move_constructor<__traits<_Types...>>;               \
                                                                                   \
      public:                                                                      \
        using __base_type::__base_type;                                            \
        using __base_type::operator=;     
        .......
        }                                       

    __copy_constructor 又继承了__move_constructor

    __move_constructor

    C++
    template
    class _LIBCPP_TEMPLATE_VIS __move_constructor;
                     
      template                                                    \
      class _LIBCPP_TEMPLATE_VIS __move_constructor<__traits<_Types...>,          \
                                                     move_constructible_trait>     \
          : public __constructor<__traits<_Types...>> {                            \
        using __base_type = __constructor<__traits<_Types...>>;                    \
                                                                                   \
      public:                                                                      \
        using __base_type::__base_type;                                            \
        using __base_type::operator=;                                              \
                                                                                   \
        __move_constructor(const __move_constructor&) = default;                   \
        move_constructor                                                           \
        ~__move_constructor() = default;                                           \
        __move_constructor& operator=(const __move_constructor&) = default;        \
        __move_constructor& operator=(__move_constructor&&) = default;             \
      }

    __move_constructor 又继承了__constructor

    __constructor

    C++
    template
    class _LIBCPP_TEMPLATE_VIS __constructor : public __destructor<_Traits> {
      using __base_type = __destructor<_Traits>;

    public:
      using __base_type::__base_type;
      using __base_type::operator=;

    protected:
      template _Ip, class _Tp, class... _Args>
      inline _LIBCPP_INLINE_VISIBILITY
      static _Tp& __construct_alt(__alt<_Ip, _Tp>& __a, _Args&&... __args) {
        ::new ((void*)_VSTD::addressof(__a))
            __alt<_Ip, _Tp>(in_place, _VSTD::forward<_Args>(__args)...);
        return __a.__value;
      }
      ....
      }

    __constructor 又继承了__destructor

    __destructor

    C++
    template
    class _LIBCPP_TEMPLATE_VIS __destructor;


      template                                                    \
      class _LIBCPP_TEMPLATE_VIS __destructor<__traits<_Types...>,                 \
                                               destructible_trait>                 \
          : public __base {                         \
        using __base_type = __base;                 \
        using __index_t = typename __base_type::__index_t;                         \
                                                                                   \
      public:                                                                      \
        using __base_type::__base_type;                                            \
        using __base_type::operator=;                                              \
                                                                                   \
        __destructor(const __destructor&) = default;                               \
        __destructor(__destructor&&) = default;                                    \
        destructor                                                                 \
        __destructor& operator=(const __destructor&) = default;                    \
        __destructor& operator=(__destructor&&) = default;                         \
                                                                                   \
      protected:                                                                   \
        ........
                                                                           \
      }

    __destructor 又继承了__base, 这个__base 是核心关键

    __base

    C++
    template <_Trait _DestructibleTrait, class... _Types>
    class _LIBCPP_TEMPLATE_VIS __base {
    public:
      using __index_t = __variant_index_t;

      inline _LIBCPP_INLINE_VISIBILITY
      explicit constexpr __base(__valueless_t tag) noexcept
          : __data(tag), __index(__variant_npos<__index_t>) {}

      template _Ip, class... _Args>
      inline _LIBCPP_INLINE_VISIBILITY
      explicit constexpr __base(in_place_index_t<_Ip>, _Args&&... __args)
          :
            __data(in_place_index<_Ip>, _VSTD::forward<_Args>(__args)...),
            __index(_Ip) {}

      inline _LIBCPP_INLINE_VISIBILITY
      constexpr bool valueless_by_exception() const noexcept {
        return index() == variant_npos;
      }

      inline _LIBCPP_INLINE_VISIBILITY
      constexpr size_t index() const noexcept {
        return __index == __variant_npos<__index_t> ? variant_npos : __index;
      }

    protected:
     
      inline _LIBCPP_INLINE_VISIBILITY
      static constexpr size_t __size() { return sizeof...(_Types); }

      __union<_DestructibleTrait, 0, _Types...> __data;
      __index_t __index;

      friend struct __access::__base;
      friend struct __visitation::__base;
    };

    跟了这么久,看了__base 数据结构定义的核心的是

    C++
    __union<_DestructibleTrait, 0, _Types...> __data;
      __index_t __index;
     

    也就是定义了__index,和一个__union,接下来搞清楚这2个,就大概知道其原理了

    __index_t

    C++
    using __index_t = __variant_index_t;
    template _NumAlts>
    using __variant_index_t = unsigned int;

    可以理解成 __index_t 就是int,  __index_t __index就是 int __index_t

    __union

    C++
    template <_Trait _DestructibleTrait, size_t _Index, class... _Types>
    union _LIBCPP_TEMPLATE_VIS __union;

    template <_Trait _DestructibleTrait, size_t _Index>
    union _LIBCPP_TEMPLATE_VIS __union<_DestructibleTrait, _Index> {};


      template _Index, class _Tp, class... _Types>                         \
      union _LIBCPP_TEMPLATE_VIS __union                                       _Index,                                  \
                                          _Tp,                                     \
                                          _Types...> {                             \
      public:                                                                      \
       ........                                 \
                                                                                   \
      private:                                                                     \
        char __dummy;                                                              \
        __alt<_Index, _Tp> __head;                                                 \
        __union __tail;                 \
                                                                                   \
        friend struct __access::__union;                                           \
      }

    看的出来__union 底层结构就是union共用体, 而整个结构tuple很相似,就是通过组合方式,拆解可变参数集,利用泛化,版本中指递归,很是巧妙,

    总结:

    经过上边对variant分析看的出来,内部逻辑十分复杂,最底层支撑的数据就是union,也就是说更高级点。

    std::variant 是如何获取取值

    我们在用法可以看到std::get<0>(v); 或者std::get(v);是等价的,都可以获取值,我也很好奇,这里边到底是怎么实现的,接下来我们看下源码是怎么实现的,

    std::get

    C++
    template
    inline
    constexpr _Tp& get(variant<_Types...>& __v) {
      return _VSTD::get<__find_exactly_one_t<_Tp, _Types...>::value>(__v);
    }

    可以看的出来,_Tp是int

    __find_exactly_one_t

    C++
    template
    struct __find_exactly_one_t
        : public __find_detail::__find_exactly_one_checked<_T1, _Args...> {
    };
    template
    struct __find_exactly_one_checked {
        static constexpr bool __matches[sizeof...(_Args)] = {is_same<_T1, _Args>::value...};
        static constexpr size_t value = __find_detail::__find_idx(0, __matches);
        static_assert(value != __not_found, "type not found in type list" );
        static_assert(value != __ambiguous, "type occurs more than once in type list");
    };

    上边源码看到__find_exactly_one_t<_Tp, _Types...>::value 是找到你要get类型,在union的位置,然好调用std::get<0>(v),也就是你根据类型获取值,最终转化成了根据序号获取值

     __matches[sizeof...(_Args)] = {is_same<_T1, _Args>::value...}; 是解包操作,存储了共用体的类型与你要的类型相比匹配的值

    __find_idx 找到__matches里边为true的值,它的index就是对应的序号

    std::get<0>

    C++
    template _Ip, class... _Types>
    inline _LIBCPP_INLINE_VISIBILITY
    _LIBCPP_AVAILABILITY_THROW_BAD_VARIANT_ACCESS
    constexpr variant_alternative_t<_Ip, variant<_Types...>>& get(
        variant<_Types...>& __v) {
       return __generic_get<_Ip>(__v);
    }

    这个要比上边定义复杂一些,这里我们就不具体跟源码,variant_alternative_t<_Ip, variant<_Types...>>拿到的是,0号元素对应类型,

    __generic_get

    C++
    template _Ip, class _Vp>
    inline _LIBCPP_INLINE_VISIBILITY
    constexpr auto&& __generic_get(_Vp&& __v) {
      using __variant_detail::__access::__variant;
      if (!__holds_alternative<_Ip>(__v)) {
        __throw_bad_variant_access();
      }
      return __variant::__get_alt<_Ip>(_VSTD::forward<_Vp>(__v)).__value;
    }
    template _Ip, class... _Types>
    inline _LIBCPP_INLINE_VISIBILITY
    constexpr bool __holds_alternative(const variant<_Types...>& __v) noexcept {
      return __v.index() == _Ip;
    }

    __holds_alternative<_Ip>(__v)  用于判断当前取的类型,跟variant初始化的时候类型是否一致,如果不一致会抛异常,这也就解释了用法中

    C++
      try {
            std::get(w); // w contains int, not float: will throw
        }
        catch (const std::bad_variant_access& ex) {
            std::cout << ex.what() << '\n';
        }

    会抛异常的根本原因

    __get_alt

    C++
    struct __variant {
      template _Ip, class _Vp>
      inline _LIBCPP_INLINE_VISIBILITY
      static constexpr auto&& __get_alt(_Vp&& __v) {
        return __base::__get_alt<_Ip>(_VSTD::forward<_Vp>(__v).__impl);
      }
    };

    struct __base {
      template _Ip, class _Vp>
      inline _LIBCPP_INLINE_VISIBILITY
      static constexpr auto&& __get_alt(_Vp&& __v) {
        return __union::__get_alt(_VSTD::forward<_Vp>(__v).__data,
                                  in_place_index<_Ip>);
      }
    };

    struct __union {
      template
      inline _LIBCPP_INLINE_VISIBILITY
      static constexpr auto&& __get_alt(_Vp&& __v, in_place_index_t<0>) {
        return _VSTD::forward<_Vp>(__v).__head;
      }

      template _Ip>
      inline _LIBCPP_INLINE_VISIBILITY
      static constexpr auto&& __get_alt(_Vp&& __v, in_place_index_t<_Ip>) {
        return __get_alt(_VSTD::forward<_Vp>(__v).__tail, in_place_index<_Ip - 1>);
      }
    };

    经过上边一层一层的着最终到了struct __union 这个结构挺有意思的, 它也是递归解包,直到匹配到了__get_alt 的上边一个版本, 然后返回__head,

    总结

    通过分析源码可以看出来__variant::__get_alt<_Ip>(_VSTD::forward<_Vp>(__v)).__value 这个最终返回了std::variant里边底层结构union对应位置的值,很是不好分析,希望对读者有所帮助

  • 相关阅读:
    2022-09-16 Android app 让图片在ScrollView里面等比例完整显示不变形,继承ImageView ,对ImageView 进行修改。
    UE4_常见动画节点学习_Two Bone IK双骨骼IK
    SpringCloudAlibaba注册中心与配置中心之利器Nacos实战与源码分析(中)
    C#教程12:结构
    前端 j s 递归
    MLX90640 红外热成像仪测温传感器模块PC端操作教程
    Canal1--搭建Canal监听数据库变化
    Flask对请求进行多个格式的响应
    【Shell脚本入门】
    现代_复习_第5章:矩阵的特征值和特征向量
  • 原文地址:https://blog.csdn.net/c553110519/article/details/126720533