Fake Variant

很久之前就想过做这样一个结构,这个结构可以存放各种各样的类型,并且可以根据需要从这个结构里取出这些类型的值。 当时用void指针做过,但是这不是类型安全的,也尝试过为所有基础类型做一个wrapper,使它们都派生自一个共同的基类(类似Java这样),然后用户自定义类型只需要继承这个基类,这样就可以利用多态实现动态分发,实际做了个heterogenous container: heterogenous container 不幸的是,这个容器存在内存泄漏(std::vector的实现里面)。

近来,又拾起尚未读完的C++ Modern Design从头开始阅读,直到昨天读到了利用TypeList生成类

template<typename TL, template<typename> class Class> class GenClasses;

template<typename T1, typename T2, template<typename> class Class>
class GenClasses<TypeList<T1, T2>, Class> : public GenClasses<T1, Class>,
		public GenClasses<T2, Class>
{};

template<typename T, template<typename> class Class>
class GenClasses : public Class<T>
{};

template<template<typename> class Class>
class GenClasses<Nil, Class>
{};

利用C++独有的特性(template template parameters)在编译期自动为指定类型生成继承关系,按照书上的例子

template<typename T> struct Holder { T value; };
using WidgetInfo GenScatterHierarchy<TYPELIST_3(int, string, Widget), Holder>;

以上代码将会生产下图所示的继承关系: inherit

看到这里,顿时感到醍醐灌顶!于是有了利用这个事实写一个variant的想法。利用C++11起提供的variadic template写下了如下代码

template<typename... Args> struct GenList;
template<> struct GenList<Nil>
{
	using type = Nil; 
};
template<typename T>
struct GenList<T>
{
	using type = TypeList<T, Nil>;
};
template<typename T, typename... Args>
struct GenList<T, Args...>
{
	using type = TypeList<T, typename GenList<Args...>::type>;
};

今天晚上有发现上面的代码并不完美,因为它会把Nil也给计算进去,比如Len<GenList<int, Nil, Nil>::type>::len会是3,而期望值是1,所以又加上的下面的代码

template<typename... Args>
struct GenList<Nil, Args...> // ignore `Nil` in Args
{
	using type = typename GenList<Args...>::type;
};

针对模板参数中的Nil进行偏特化。

再为FakeVariant类添加了emplace方法,但是原有的set方法同名,作重载用,用在Non-Copyable结构上。
至此,一个简单的variant就可以用了: variant

源码请点这里


最后,感谢 Andrei Alexandrescu 为我们带来这样一本传世之作!

PS: C++ 17已提供variant支持,以及anyoptional,但是不幸的是目前GCC 6.2.1中并没有发现variant支持,预计在GCC7实验性支持以上3个特性,详情点击这里