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对应位置的值,很是不好分析,希望对读者有所帮助

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