用c++11打造好用的variant(更新)(一)

2014-11-24 09:25:38 · 作者: · 浏览: 2
 关于variant的实现参考我前面的博文,不过这第一个版本还不够完善,主要有这几个问题:
内部的缓冲区是原始的char[],没有考虑内存对齐;
没有visit功能。
没有考虑赋值构造函数的问题,存在隐患。
  这次将解决以上问题,还将进一步增强variant的功能。增加的功能有:
通过索引位置获取类型。
通过类型获取索引位置。
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。alignof是vs2013 ctp中才支持的,如果没有该版本则可以用std::alignment_of来代替,可以通过std::alignment_of::value来获取内存对齐大小。故std::aligned_storage可以这样声明:std::aligned_storage::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(old_v)));
左值版本Helper_t::copy的内部是这样的:new (new_v) T(*reinterpret_cast(old_v));
右值版本可以直接将原对象move走,左值版本则需要拷贝原来的对象。
variant的visit功能
  boost.variant中可以使用apply_visitior来访问variant中实际的类型,具体的做法是先创建一个从boost::static_visitor派生的访问者类,这个类中定义了访问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(