boost中的variant的基本用法:
typedef variant
vt v = 1;
v = '2';
v = 12.32;
用variant一个好处是可以擦除类型,不同类型的值都统一成一个variant,虽然这个variant只能存放已定义的类型,但这在很多时候已经够用了。 取值的时候,通过get
打造variant需要解决的问题:
第一,要在内部定义一个char缓冲区。
缓冲区用来存放variant的值,这个值是variant定义的多种类型中的某种类型的值,因此,这个缓冲区要足够大,能够存放类型最大(sizeof(Type))的值才可以,这个缓冲区的大小还必须在编译期计算出来。因此需要首先要解决的是variant值存放的缓冲区定义的问题。
第二,要解决赋值的问题。
将值赋给vairiant时,需要将该值的类型ID记录下来,以便在后面根据类型取值。将值保存到内部缓冲区时,还需要用palcement new在缓冲区创建对象。另外,还要解决一个问题,就是赋值时需要检查variant中已定义的类型中是否含有该类型,如果没有则编译不通过,以保证赋值是合法的。
第三,要解决取值的问题。
通过类型取值时,要判断类型是否匹配,如果不匹配,将详情打印出来,方便调试。
打造variant的关键技术:
1.找出最大的typesize
第一个问题中需要解决的问题是如何找出多种类型中,size最大的那个类型的size。看看如何从多种类型中找出最大类型的size。
template
struct MaxType : std::integral_constant
{};
template
struct MaxType
通过这个MaxType就可以在编译期获取类型中最大的maxsize了:MaxType
2.类型检查和缓冲区中创建对象
第二个问题中需要解决两个问题,1.检查赋值的类型是否在已定义的类型中;2.在缓冲区中创建对象及析构;
先看看如何判断类型列表中是否含有某种类型:
template < typename T, typename... List >
struct Contains : std::true_type {};
template < typename T, typename Head, typename... Rest >
struct Contains
: std::conditional< std::is_same
Contains
template < typename T >
struct Contains
通过bool值Contains
再看看如何在缓冲区中创建对象。
通过placement new在该缓冲区上创建对象,new(data) T(value);其中data表示一个char缓冲区,T表示某种类型。在缓冲区上创建的对象还必须通过~T去析构,因此还需要一个析构vairiant的帮助类:
template
struct VariantHelper;
template
struct VariantHelper
inline static void Destroy(type_index id, void * data)
{
if (id == type_index(typeid(T)))
((T*) (data))->~T();
else
VariantHelper
}
};
template<> struct VariantHelper<> {
inline static void Destroy(type_index id, void * data) { }
};
通过VariantHelper::Destroy函数就可以析构variant了。
3.取值问题
第三个问题中需要解决取值问题,如果发生异常,就打印出详细信息。这个就比较简单了,看后面的实现代码就行了。
c++11中完整的variant是如何实现的:
#include
#include
#include
using namespace std;
template
struct MaxType : std::integral_constant
{};
template
struct MaxType
template < typename T, typename... List >
struct Contains : std::true_type {};
template < typename T, typename Head, typename... Rest > Rest...>>::type{}; template < typename T > template template template<> struct VariantHelper<> { template Variant(void) :m_typeIndex(typeid(void)) ~Variant() template template m_typeIndex.name() << endl; return *(T*) (&m_data); template std::remove_reference private:
struct Contains
: std::conditional< std::is_same
struct Contains
struct VariantHelper;
struct VariantHelper
inline static void Destroy(type_index id, void * data)
{
if (id == type_index(typeid(T)))
((T*) (data))->~T();
else
VariantHelper
}
};
inline static void Destroy(type_index id, void * data) { }
};
class Variant
{
typedef VariantHelper
public:
{
}
{
Helper_t::Destroy(m_typeIndex, &m_data);
}
bool Is()
{
return (m_typeIndex == type_index(typeid(T)));
}
T& Get()
{
if (!Is
{
cout << typeid(T).name() << " is not defined. " << "current type is " <<
throw std::bad_cast();
}
}
Variant(T&& value) : m_typeIndex(type_index(typeid(T)))
{
Helper_t::Destroy(m_typeIndex, &m_data);
typedef typename std::remove_reference
new(m_data) U(std::forward
}
char m_data[MaxType
std::type_index m_typeIndex;
};
测试代码:
void TestVariant()
{
typedef Variant
int x = 10;
cv v =x;
v = 1;
v = 1.123;
v = "";//compile error
v.Get
v.Get
v.Get
int.
v.Is
}
总结:c++11实现的variant在用法上和boost.variant保持一致,但实现更简洁,50行代码搞定。而且还能在抛出异常时提示详细信息,方便调试。
c++11 boost技术交流群:296561497,欢迎大家来交流技术。