关于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<class T>
struct remove_all_extents { typedef T type;}; template<class T>
struct remove_all_extents<T[]> {
typedef typename remove_all_extents<T>::type type;
}; template<class Tp, std::size_t N>
struct remove_all_extents<T[N]> {
typedef typename remove_all_extents<T>::type type; template< class T >
struct alignment_of : std::integral_constant<
std::size_t,
alignof(typename std::remove_all_extents<T>::type)
> {};
  • alignof和sizeof有点类似,它可以应用于变长类型,比如alignof(Args)...,而std::alignment_of则不行。

variant赋值构造函数的问题

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

    Variant(Variant<Types...>&& old) : m_typeIndex(old.m_typeIndex)
{
Helper_t::move(old.m_typeIndex, &old.m_data, &m_data);
} Variant(const Variant<Types...>& 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<int,short,double,std::string>
struct VariantVisitor : public boost::static_visitor<void>
{
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<int,short,double,std::string> v = ;
boost::apply_visitor(visitor, it->first); //将输出int

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

typedef Variant<int, double, string, int> cv;
cv v = ;
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<int, double, string, int> cv;
cv v = ;
cout << typeid(cv::IndexType<>).name() << endl; //将输出double
int i = v.GetIndexOf<string>(); //将输出索引位置2

c++11版本的variant

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

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

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

/*
template<typename T, typename... Args>
struct MaxAlign : std::integral_constant<int,
(std::alignment_of<T>::value >MaxAlign<Args...>::value ? std::alignment_of<T>::value : MaxAlign<Args...>::value) >
{}; template<typename T>
struct MaxAlign<T> : std::integral_constant<int, std::alignment_of<T>::value >{}; */ //是否包含某个类型
template < typename T, typename... List >
struct Contains : std::true_type {}; template < typename T, typename Head, typename... Rest >
struct Contains<T, Head, Rest...>
: std::conditional< std::is_same<T, Head>::value, std::true_type, Contains<T,Rest... >> ::type{}; template < typename T >
struct Contains<T> : std::false_type{}; //获取第一个T的索引位置
// Forward
template<typename Type, typename... Types>
struct GetLeftSize; // Declaration
template<typename Type, typename First, typename... Types>
struct GetLeftSize<Type, First, Types...> : GetLeftSize<Type, Types...>
{
}; // Specialized
template<typename Type, typename... Types>
struct GetLeftSize<Type, Type, Types...> : std::integral_constant<int, sizeof...(Types)>
{
//static const int ID = sizeof...(Types);
}; template<typename Type>
struct GetLeftSize<Type> : std::integral_constant<int, ->
{
//static const int ID = -1;
}; template<typename T, typename... Types>
struct Index : std::integral_constant<int, sizeof...(Types) - GetLeftSize<T, Types...>::value - >{}; //根据索引获取索引位置的类型
// Forward declaration
template<int index, typename... Types>
struct IndexType; // Declaration
template<int index, typename First, typename... Types>
struct IndexType<index, First, Types...> : IndexType<index - , Types...>
{
}; // Specialized
template<typename First, typename... Types>
struct IndexType<, First, Types...>
{
typedef First DataType;
}; template<typename... Args>
struct VariantHelper; template<typename T, typename... Args>
struct VariantHelper<T, Args...> {
inline static void Destroy(type_index id, void * data)
{
if (id == type_index(typeid(T)))
//((T*) (data))->~T();
reinterpret_cast<T*>(data)->~T();
else
VariantHelper<Args...>::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<T*>(old_v)));
else
VariantHelper<Args...>::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<const T*>(old_v));
else
VariantHelper<Args...>::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<typename... Types>
class Variant
{
typedef VariantHelper<Types...> Helper_t; enum
{
data_size = IntegerMax<sizeof(Types)...>::value,
//align_size = IntegerMax<alignof(Types)...>::value
align_size = MaxAlign<Types...>::value //ctp才有alignof, 为了兼容用此版本
};
using data_t = typename std::aligned_storage<data_size, align_size>::type;
public:
template<int index>
using IndexType = typename IndexType<index, Types...>::DataType; Variant(void) :m_typeIndex(typeid(void)), m_index(-)
{
} ~Variant()
{
Helper_t::Destroy(m_typeIndex, &m_data);
} Variant(Variant<Types...>&& old) : m_typeIndex(old.m_typeIndex)
{
Helper_t::move(old.m_typeIndex, &old.m_data, &m_data);
} Variant(const Variant<Types...>& 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 <class T,
class = typename std::enable_if<Contains<typename std::remove_reference<T>::type, Types...>::value>::type>
Variant(T&& value) : m_typeIndex(typeid(void))
{
Helper_t::Destroy(m_typeIndex, &m_data);
typedef typename std::remove_reference<T>::type U;
new(&m_data) U(std::forward<T>(value));
m_typeIndex = type_index(typeid(T));
} template<typename T>
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 T>
typename std::decay<T>::type& Get()
{
using U = typename std::decay<T>::type;
if (!Is<U>())
{
cout << typeid(U).name() << " is not defined. " << "current type is " <<
m_typeIndex.name() << endl;
throw std::bad_cast();
} return *(U*) (&m_data);
} template<typename T>
int GetIndexOf()
{
return Index<T, Types...>::value;
} template<typename F>
void Visit(F&& f)
{
using T = typename function_traits<F>::arg<>::type;
if (Is<T>())
f(Get<T>());
} template<typename F, typename... Rest>
void Visit(F&& f, Rest&&... rest)
{
using T = typename function_traits<F>::arg<>::type;
if (Is<T>())
Visit(std::forward<F>(f));
else
Visit(std::forward<Rest>(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<int, double, string, int> cv;
//根据index获取类型
cout << typeid(cv::IndexType<>).name() << endl; //根据类型获取索引
cv v=;
int i = v.GetIndexOf<string>(); //通过一组lambda访问vairant
v.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,欢迎大家来交流技术。

(原创)用c++11打造好用的variant(更新)的更多相关文章

  1. (原创)用c++11打造好用的variant

    variant类似于union,它能代表定义的多种类型,允许将不同类型的值赋给它.它的具体类型是在初始化赋值时确定.boost中的variant的基本用法: typedef variant<in ...

  2. [原创] Easy SysLite V1.2 (2016.5.29更新,新增加WIN10支持,一个程序适配所有系统减肥)

    [原创] Easy SysLite V1.2 (2016.5.29更新,新增加WIN10支持,一个程序适配所有系统减肥) nohacks 发表于 2016-5-29 17:12:51 https:// ...

  3. (原创)用c++11打造好用的any

    上一篇博文用c++11实现了variant,有童鞋说何不把any也实现一把,我正有此意,它的兄弟variant已经实现了,any也顺便打包实现了吧.其实boost.any已经挺好了,就是转换异常时,看 ...

  4. (原创)用c++11打造类似于python的range

    python中的range函数表示一个连续的有序序列,range使用起来很方便,因为在定义时就隐含了初始化过程,因为只需要给begin()和end()或者仅仅一个end(),就能表示一个连续的序列.还 ...

  5. 【原创】C++11:左值和右值(深度分析)

    ——原创,引用请附带博客地址 2019-12-06 23:42:18 这篇文章分析的还是不行,先暂时放在这以后再更新. 本篇比较长,需要耐心阅读 以一个实际问题开始分析 class Sub{} Sub ...

  6. 原创:goldengate从11.2升级到12.1.2

    goldengate从11.2升级到12.1.2 1.停止抽取进程 GGSCI (001.oracle.drs.dc.com) 286> stop EXTSJ01 2. 停止投递和复制进程 等待 ...

  7. 用c++11打造类似于python的range

    python中的range函数表示一个连续的有序序列,range使用起来很方便,因为在定义时就隐含了初始化过程,因为只需要给begin()和end()或者仅仅一个end(),就能表示一个连续的序列.还 ...

  8. [原创]EF架构随心所欲打造属于你自己的DbModel

    前言 我们都知道EF可以生成Dbmodel,系统生成的Model有时候并不是我们想要的,如何我们要生成自己的Model,那么久需要我们手动的去修改T4模版,T4是对“Text Template Tra ...

  9. (原创)c++11中 function/lamda的链式调用

    关于链式调用,比较典型的例子是c#中的linq,不过c#中的linq还只是一些特定函数的链式调用.c++中的链式调用更少见因为实现起来比较复杂.c++11支持了lamda和function,在一些延迟 ...

随机推荐

  1. Arduino通过I2C(SSD1306)驱动0.96寸12864OLED

    I2C驱动的128x64 OLED I2C (Inter-Integrated Circuit) 集成电路总线是I2CBus的简称, 是一种串行通信总线, 使用多主从架构. 飞利浦公司在1980年代为 ...

  2. (原)InsightFace及其mxnet代码

    转载请注明出处: http://www.cnblogs.com/darkknightzh/p/8525287.html 论文 InsightFace : Additive Angular Margin ...

  3. mysql之limit m,n

    limit是mysql的语法 select * from table limit [m],n; 其中,m—— [m]为可选,如果填写表示skip步长,即跳过m条. n——显示条数.指从第m+1条记录开 ...

  4. 常用Raspberry Pi周边传感器的使用教程(转)

    转:http://bbs.xiaomi.cn/thread-7797152-1-1.html 在Raspberry Pi 的使用和开发过程中,你可能时常需要一些硬件和传感器等来支持你的开发工作,例如, ...

  5. 归并排序(C++实现)

         归并排序是利用"归并"技术来进行排序.归并是指将若干个已排序的子文件合并成一个有序的文件.常见的归并排序有两路归并排序(Merge Sort),多相归并排序(Polyph ...

  6. openstack neutron 二/三层网络实现

    引用声明:https://zhangchenchen.github.io/2017/02/12/neutron-layer2-3-realization-discovry/ 一.概述 Neutron是 ...

  7. 【Servlet】使用org.eclipse.jetty实现小型的Servlet服务器

    import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.http.HttpSer ...

  8. 【Oracle】Oracle中复合数据类型

    1,常见的操作数据库的技术有那些 jdbc     使用java 访问数据库的技术    PLSQL  (procedure  过程化sql) 在数据库内部操作数据的技术    proc/c++    ...

  9. Mac PATH你所需要了解的

    关于Mac环境变量你需要知道: 0.何为环境变量? 1.如何查看环境变量? 2.如何配置环境变量? 3.如何重置环境变量? 0.环境变量: 所谓环境变量,$PATH,应用于Linux,OS X,Uni ...

  10. SharePoint利用HttpModule的Init方法实现全局初始化

    接上篇 我们知道,HttpRuntime中会对每一个Request创建一个HttpApplication对象(HttpApplicationFactory从一个HttpApplication池来拿). ...