博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
(原创)用c++11打造好用的variant(更新)
阅读量:6977 次
发布时间:2019-06-27

本文共 9955 字,大约阅读时间需要 33 分钟。

  关于variant的实现参考我前面的,不过这第一个版本还不够完善,主要有这几个问题:

  1. 内部的缓冲区是原始的char[],没有考虑内存对齐;
  2. 没有visit功能。
  3. 没有考虑赋值构造函数的问题,存在隐患。

  这次将解决以上问题,还将进一步增强variant的功能。增加的功能有:

  1. 通过索引位置获取类型。
  2. 通过类型获取索引位置。

c++11的内存对齐

  关于内存对齐的问题,将用c++11的std::aligned_storage来代替char[]数组,它的原型是:

template< std::size_t Len, std::size_t Align = /*default-alignment*/ >struct aligned_storage;

  其中Len表示所存储类型的size,Align表示该类型内存对齐的大小,通过sizeof(T)可以获取T的size,通过alignof(T)可以获取T内存对齐大小,所以std::aligned_storage的声明是这样的:std::aligned_storage<sizeof(T), alignof(T)>。alignof是vs2013 ctp中才支持的,如果没有该版本则可以用std::alignment_of来代替,可以通过std::alignment_of<T>::value来获取内存对齐大小。故std::aligned_storage可以这样声明:std::aligned_storage<sizeof(T), std::alignment_of<T>::value>。

  这里要说一下alignof和std::alignment_of的区别,主要区别:

  • std::alignment_of对于数组来说,是获取数组中元素类型内存对齐大小,如果非数组则是类型本身的内存对齐大小,因此使用时要注意这一点。其实std::alignment_of可以由align来实现:
template
struct remove_all_extents { typedef T type;}; template
struct remove_all_extents
{ typedef typename remove_all_extents
::type type;}; template
struct remove_all_extents
{ typedef typename remove_all_extents
::type type;template< class T >struct alignment_of : std::integral_constant< std::size_t, alignof(typename std::remove_all_extents
::type) > {};
  • alignof和sizeof有点类似,它可以应用于变长类型,比如alignof(Args)...,而std::alignment_of则不行。

variant赋值构造函数的问题

  variant如果通过默认赋值函数赋值的话会造成两个variant的缓冲区都是一个,会导致重复析构。variant的赋值函数需要做两件事,第一是借助于赋值的variant的缓冲区取得其实际的类型;第二用赋值的variant种实际的类型构造出当前variant的实际类型。赋值函数的左值和右值版本的实现如下:

Variant(Variant
&& old) : m_typeIndex(old.m_typeIndex) { Helper_t::move(old.m_typeIndex, &old.m_data, &m_data); } Variant(const Variant
& old) : m_typeIndex(old.m_typeIndex) { Helper_t::copy(old.m_typeIndex, &old.m_data, &m_data); }

右值版本Helper_t::move的内部是这样的:new (new_v) T(std::move(*reinterpret_cast<T*>(old_v)));

左值版本Helper_t::copy的内部是这样的:new (new_v) T(*reinterpret_cast<const T*>(old_v));

右值版本可以直接将原对象move走,左值版本则需要拷贝原来的对象。

variant的visit功能

  boost.variant中可以使用apply_visitior来访问variant中实际的类型,具体的做法是先创建一个从boost::static_visitor<T>派生的访问者类,这个类中定义了访问variant各个类型的方法,接着将这个访问者对象和vairant对象传到boost::apply_visitor(visitor, it->first)实现vairant的访问。一个简单的例子是这样的:

//创建一个访问者类,这个类可以访问vairant
struct VariantVisitor : public boost::static_visitor
{ void operator() (int a) { cout << "int" << endl; } void operator() (short val) { cout << "short" << endl; } void operator() (double val) { cout << "double" << endl; } void operator() (std::string val) { cout << "string" << endl; }};boost::variant
v = 1;boost::apply_visitor(visitor, it->first); //将输出int

  实际上这也是标准的访问者模式的实现,这种方式虽然可以实现对variant内部实际类型的访问,但是有一个缺点是有点繁琐,还需要定义一个函数对象,不够方便。c++11中有了lambda表达式了,是不是可以用lambda表达式来替代函数对象呢?如果直接通过一组lambda表达式来访问实际类型的话,那将是更直观而方便的访问方式,不再需要从boost::static_visitor派生了,也不需要写一堆重载运算符。我希望这样访问vairant的实际类型:

typedef Variant
cv;cv v = 10;v.Visit([&](double i){cout << i << endl; }, [](short i){cout << i << endl; }, [=](int i){cout << i << endl; },[](string i){cout << i << endl; });//结果将输出10

这种方式比boost的访问方式更简洁直观。这个版本中将增加这种内置的访问方式。

比boost.variant多的更能

  这个版本增加了通过索引获取类型和通过类型获取索引的功能,你可以从variant中获取更多信息,boost.variant中是没有这两个接口的。

typedef Variant
cv;cv v = 10;cout << typeid(cv::IndexType<1>).name() << endl; //将输出doubleint i = v.GetIndexOf
(); //将输出索引位置2

c++11版本的variant

  下面来看看c++11版本的vairant的具体实现了:

#include 
#include
#include
using namespace std;template
struct function_traits : public function_traits
{};// For generic types, directly use the result of the signature of its 'operator()'template
struct function_traits
// we specialize for pointers to member function{ enum { arity = sizeof...(Args) }; // arity is the number of arguments. typedef ReturnType result_type; template
struct arg { typedef typename std::tuple_element
>::type type; // the i-th argument is equivalent to the i-th tuple element of a tuple // composed of those arguments. }; typedef std::function
FunType; typedef std::tuple
ArgTupleType;};//获取最大的整数template
struct IntegerMax;template
struct IntegerMax
: std::integral_constant
{ //static const size_t value = arg; //enum{value = arg};};//获取最大的aligntemplate
struct IntegerMax
: std::integral_constant
= arg2 ? IntegerMax
::value : IntegerMax
::value >{ /*static const size_t value = arg1 >= arg2 ? static_max
::value : static_max
::value;*/};

  template<typename... Args>

  struct MaxAlign : std::integral_constant<int, IntegerMax<std::alignment_of<Args>::value...>::value>{};

/*template
struct MaxAlign : std::integral_constant
::value >MaxAlign
::value ? std::alignment_of
::value : MaxAlign
::value) >{};template
struct MaxAlign
: std::integral_constant
::value >{}; *///是否包含某个类型template < typename T, typename... List >struct Contains : std::true_type {};template < typename T, typename Head, typename... Rest >struct Contains
: std::conditional< std::is_same
::value, std::true_type, Contains
> ::type{};template < typename T >struct Contains
: std::false_type{};//获取第一个T的索引位置// Forwardtemplate
struct GetLeftSize;// Declarationtemplate
struct GetLeftSize
: GetLeftSize
{};// Specializedtemplate
struct GetLeftSize
: std::integral_constant
{ //static const int ID = sizeof...(Types);};template
struct GetLeftSize
: std::integral_constant
{ //static const int ID = -1;};template
struct Index : std::integral_constant
::value - 1>{};//根据索引获取索引位置的类型// Forward declarationtemplate
struct IndexType;// Declarationtemplate
struct IndexType
: IndexType
{};// Specializedtemplate
struct IndexType<0, First, Types...>{ typedef First DataType;};template
struct VariantHelper;template
struct VariantHelper
{ inline static void Destroy(type_index id, void * data) { if (id == type_index(typeid(T))) //((T*) (data))->~T(); reinterpret_cast
(data)->~T(); else VariantHelper
::Destroy(id, data); } inline static void move(type_index old_t, void * old_v, void * new_v) { if (old_t == type_index(typeid(T))) new (new_v) T(std::move(*reinterpret_cast
(old_v))); else VariantHelper
::move(old_t, old_v, new_v); } inline static void copy(type_index old_t, const void * old_v, void * new_v) { if (old_t == type_index(typeid(T))) new (new_v) T(*reinterpret_cast
(old_v)); else VariantHelper
::copy(old_t, old_v, new_v); }};template<> struct VariantHelper<> { inline static void Destroy(type_index id, void * data) { } inline static void move(type_index old_t, void * old_v, void * new_v) { } inline static void copy(type_index old_t, const void * old_v, void * new_v) { }};template
class Variant{ typedef VariantHelper
Helper_t; enum { data_size = IntegerMax
::value, //align_size = IntegerMax
::value align_size = MaxAlign
::value //ctp才有alignof, 为了兼容用此版本 }; using data_t = typename std::aligned_storage
::type;public: template
using IndexType = typename IndexType
::DataType; Variant(void) :m_typeIndex(typeid(void)), m_index(-1) { } ~Variant() { Helper_t::Destroy(m_typeIndex, &m_data); } Variant(Variant
&& old) : m_typeIndex(old.m_typeIndex) { Helper_t::move(old.m_typeIndex, &old.m_data, &m_data); } Variant(const Variant
& old) : m_typeIndex(old.m_typeIndex) { Helper_t::copy(old.m_typeIndex, &old.m_data, &m_data); }

    Variant& operator=(const Variant& old)

    {
      Helper_t::copy(old.m_typeIndex, &old.m_data, &m_data);

      m_typeIndex = old.m_typeIndex;

      return *this;
    }

 

    Variant& operator=(Variant&& old)

    {
      Helper_t::move(old.m_typeIndex, &old.m_data, &m_data);

      m_typeIndex = old.m_typeIndex;

      return *this;
    }

template 
::type, Types...>::value>::type> Variant(T&& value) : m_typeIndex(typeid(void)) { Helper_t::Destroy(m_typeIndex, &m_data); typedef typename std::remove_reference
::type U; new(&m_data) U(std::forward
(value)); m_typeIndex = type_index(typeid(T)); } template
bool Is() const { return (m_typeIndex == type_index(typeid(T))); } bool Empty() const { return m_typeIndex == type_index(typeid(void)); } type_index Type() const { return m_typeIndex; } template
typename std::decay
::type& Get() { using U = typename std::decay
::type; if (!Is
()) { cout << typeid(U).name() << " is not defined. " << "current type is " << m_typeIndex.name() << endl; throw std::bad_cast(); } return *(U*) (&m_data); } template
int GetIndexOf() { return Index
::value; } template
void Visit(F&& f) { using T = typename function_traits
::arg<0>::type; if (Is
()) f(Get
()); } template
void Visit(F&& f, Rest&&... rest) { using T = typename function_traits
::arg<0>::type; if (Is
()) Visit(std::forward
(f)); else Visit(std::forward
(rest)...); } bool operator==(const Variant& rhs) const { return m_typeIndex == rhs.m_typeIndex; } bool operator<(const Variant& rhs) const { return m_typeIndex < rhs.m_typeIndex; }private: data_t m_data; std::type_index m_typeIndex;//类型ID};

 

  测试代码:

typedef Variant
cv;//根据index获取类型cout << typeid(cv::IndexType<1>).name() << endl;//根据类型获取索引cv v=10;int i = v.GetIndexOf
();//通过一组lambda访问vairantv.Visit([&](double i){cout << i << endl; }, [&](short i){cout << i << endl; }, [](int i){cout << i << endl; }, [](string i){cout << i << endl; });bool emp1 = v.Empty();cout << v.Type().name() << endl;

  c++11版本的vairant不仅仅比boost的variant更好用也更强大,经过测试发现性能也优于boost.variant,因此可以在项目中用这个c++11版本的variant替代boost的variant。实际上我的并行计算库中已经用自己的variant和any代替了boost.variant和boost.any,从而消除了对boost的依赖。

 

c++11 boost技术交流群:296561497,欢迎大家来交流技术。

转载地址:http://pgkpl.baihongyu.com/

你可能感兴趣的文章
mmap学习
查看>>
X3D中Profile如何翻译
查看>>
7.14. revision
查看>>
第 175 章 Open Source Requirements Management Tool
查看>>
CentOS7安装配置redis-3.0.0
查看>>
SQL server 专业词汇
查看>>
Selenium2+python自动化25-js处理日历控件(修改readonly属性)
查看>>
ArcGIS制图之Sub Points点抽稀
查看>>
[高中作文赏析]假如真的有时光隧道
查看>>
selenium+python自动化81-html报告优化(饼图+失败重跑+兼容python2&3)
查看>>
thinkphp pathinfo nginx 无法加载模块:Index
查看>>
如何用 Graylog 管理日志?- 每天5分钟玩转 Docker 容器技术(93)
查看>>
单例模式
查看>>
iOS多线程编程之NSOperation和NSOperationQueue的使用
查看>>
SAP QM 'QM System' 有什么控制作用?
查看>>
Health Check in eShop -- 解析微软微服务架构Demo(五)
查看>>
项目沟通管理计划
查看>>
[20160608]自治事务引起死锁.txt
查看>>
AliGenie AR Fuels the Ali New Retail Strategy - Interactive Marketing Activities
查看>>
一个最简单的通过WireShark破解SSL加密网络数据包的方法
查看>>