"The variant class template is a safe, generic, stack-based discriminated union container, offering a simple solution for manipulating an object from a heterogeneous set of types in a uniform manner. Whereas standard containers such as std::vector may be thought of as "multi-value, single type," variant is "multi-type, single value.
Problem
Many times, during the development of a C++ program, the programmer finds himself in need of manipulating several distinct types in a uniform manner. Indeed, C++ features direct language support for such types through its union keyword:
union { int i; double d; } u;
u.d = 3.14;
u.i = 3; // overwrites u.d (OK: u.d is a POD type)
C++'s union construct, however, is nearly useless in an object-oriented environment. The construct entered the language primarily as a means for preserving compatibility with C, which supports only POD (Plain Old Data) types, and so does not accept types exhibiting non-trivial construction or destruction:
union {
int i;
std::string s; // illegal: std::string is not a POD type!
} u;
union 的成员只能是POD类型。
//<boost程序完全开发指南>
#include <boost/variant.hpp>
#include <string>
#include <iostream>
using namespace boost;
int main()
{
typedef variant<int, double, std::string> var_t;
var_t v;
assert(v.type() == typeid(int));
assert(v.which() == 0);
v = "variant demo";
std::cout << *get<std::string>(&v) << std::endl; //使用get()函数取值
try
{
std::cout << get<double>(v) << std::endl;
}
catch(bad_get &)
{
std::cout << "bad_get" << std::endl;
}
std::cin.get();
}
结果
variant demo
bad_get
const std::type_info& type() const;
返回type_info对象,可判别variant当前的数据类型
“我们也可以使用自由函数get()来获取variant的值:
cout << get<string>(v)”
“但get()函数通常不是最方便的访问方法,”它存在类型不安全的隐患,操作时必须查询variant当前值的类型。
改进版
//<me>
#include <boost/variant.hpp>
#include <string>
#include <iostream>
using namespace boost;
typedef variant<int, double, std::string> var_t;
void var_print(var_t & v)
{
if(v.type() == typeid(int))
{
std::cout << get<int>(v) << std::endl;
}
else if(v.type() == typeid(double))
{
std::cout << get<double>(v) << std::endl;
}
else if(v.type() == typeid(std::string))
{
std::cout << get<std::string>(v) << std::endl;
}
else
{
std::cout << "error type" << std::endl;
}
}
int main()
{
var_t v;
v = "variant demo";
var_print(v);
v = 3.14;
var_print(v);
v = 909;
var_print(v);
std::cin.get();
}
结果:
variant demo
3.14
909
函数var_print()将输入的variant对象输出,它使用了RTTI技术,效率低,并且一旦variant的模板参数发生变化,var_print()也必须改动,其if_else的处理结果也很不优雅。
Variant基于访问者模式提供了模板类static_visitor"
//<.修改代码>
#include <boost/variant.hpp>
#include <string>
#include <iostream>
using namespace boost;
typedef variant<int, double, std::string> var_t;
class print_visitor : public static_visitor<void>
{
public:
void operator()(int i) const
{
std::cout << "It's an int: " << i << '\n';
}
void operator()(std::string s) const
{
std::cout << "It's a std::string: " << s << '\n';
}
void operator()(double d) const
{
std::cout << "It's a double: " << d << '\n';
}
};
int main()
{
print_visitor v;
var_t var;
var = "variant demo";
boost::apply_visitor(v, var);
var = 3.14;
boost::apply_visitor(v, var);
var = 909;
boost::apply_visitor(v, var);
std::cin.get();
}
结果:
It's a std::string: variant demo
It's a double: 3.14
It's an int: 909
二元访问器
“之前的访问器都只能操作一个variant对象,但操作两个variant对象的访问器也允许的,这在某些情况下是有用的,比如对两个variant进行比较。”
"注意,如果调用 get 失败(当 my_first_variant 所含值不是类型 int 时就会发生),会抛出一个类型为 boost::bad_get 的异常。"