Boost之Variant

"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 的异常。"




你可能感兴趣的:(c,String,Types)