一、提要
Boost.Variant 提供了一个类似于 union 的名为 boost::variant 的类。您可以将不同类型的值存储在 boost::variant 变量中。在任何时候只能存储一个值。分配新值时,旧值将被覆盖。但是,新值的类型可能与旧值不同。唯一的要求是这些类型必须作为模板参数传递给 boost::variant,这样它们才能为 boost::variant 变量所知。
boost::variant 支持任何类型。例如,可以将 std::string 存储在 boost::variant 变量中——这在 C++11 之前使用联合是不可能的。在 C++11 中,对联合的要求被放宽了。现在联合可以包含一个 std::string。因为 std::string 必须使用placement new 进行初始化,并且必须通过显式调用析构函数来销毁,所以使用 boost::variant 仍然有意义,即使在 C++11 开发环境中也是如此。
二、示例
示例 24.1。使用 boost::variant
#include <boost/variant.hpp>
#include <string>
int main()
{
boost::variant<double, char, std::string> v;
v = 3.14;
v = 'A';
v = "Boost";
}
boost::variant 在 boost/variant.hpp 中定义。因为 boost::variant 是一个模板,所以必须至少指定一个参数。一个或多个模板参数指定支持的类型。在示例 24.1 中,v 可以存储类型为 double、char 或 std::string 的值。但是,如果您尝试将 int 类型的值分配给 v,则生成的代码将无法编译。
示例 24.2。使用 boost::get() 访问 boost::variant 中的值
#include <boost/variant.hpp>
#include <string>
#include <iostream>
int main()
{
boost::variant<double, char, std::string> v;
v = 3.14;
std::cout << boost::get<double>(v) << '\n';
v = 'A';
std::cout << boost::get<char>(v) << '\n';
v = "Boost";
std::cout << boost::get<std::string>(v) << '\n';
}
要显示 v 的存储值,请使用独立函数 boost::get()(参见示例 24.2)。
boost::get() 期望对应变量的有效类型之一作为模板参数。指定无效类型将导致运行时错误,因为类型验证不会在编译时进行。
boost::variant 类型的变量可以写入标准输出流等流,从而绕过运行时错误的风险(参见示例 24.3)。
示例 24.3。在流上直接输出 boost::variant
#include <boost/variant.hpp>
#include <string>
#include <iostream>
int main()
{
boost::variant<double, char, std::string> v;
v = 3.14;
std::cout << v << '\n';
v = 'A';
std::cout << v << '\n';
v = "Boost";
std::cout << v << '\n';
}
对于类型安全的访问,Boost.Variant 提供了一个名为 boost::apply_visitor() 的函数。
示例 24.4。使用访问者来提升::variant
#include <boost/variant.hpp>
#include <string>
#include <iostream>
struct output : public boost::static_visitor<>
{
void operator()(double d) const { std::cout << d << '\n'; }
void operator()(char c) const { std::cout << c << '\n'; }
void operator()(std::string s) const { std::cout << s << '\n'; }
};
int main()
{
boost::variant<double, char, std::string> v;
v = 3.14;
boost::apply_visitor(output{}, v);
v = 'A';
boost::apply_visitor(output{}, v);
v = "Boost";
boost::apply_visitor(output{}, v);
}
作为其第一个参数,boost::apply_visitor() 期望从 boost::static_visitor 派生的类的对象。此类必须为它所作用的 boost::variant 变量使用的每种类型重载 operator()。因此,在示例 24.4 中,运算符重载了 3 次,因为 v 支持 double、char 和 std::string 类型。
boost::static_visitor 是一个模板。 operator() 的返回值类型必须指定为模板参数。如果运算符没有返回值,则不需要模板参数,如示例中所示。
传递给 boost::apply_visitor() 的第二个参数是一个 boost::variant 变量。
boost::apply_visitor() 自动为第一个参数调用 operator() ,该参数与当前存储在第二个参数中的值的类型相匹配。这意味着每次调用 boost::apply_visitor() 时,示例程序都会使用不同的重载运算符——首先是用于 double 的运算符,然后是用于 char 的运算符,最后是用于 std::string 的运算符。
boost::apply_visitor() 的优点不仅在于自动调用正确的运算符。此外,boost::apply_visitor() 确保为 boost::variant 变量支持的每种类型都提供了重载运算符。如果未定义三个重载运算符之一,则无法编译代码。
如果重载的运算符在功能上是等效的,则可以使用模板简化代码(参见示例 24.5)。
示例 24.5。使用带有 boost::variant 函数模板的访问者
#include <boost/variant.hpp>
#include <string>
#include <iostream>
struct output : public boost::static_visitor<>
{
template <typename T>
void operator()(T t) const { std::cout << t << '\n'; }
};
int main()
{
boost::variant<double, char, std::string> v;
v = 3.14;
boost::apply_visitor(output{}, v);
v = 'A';
boost::apply_visitor(output{}, v);
v = "Boost";
boost::apply_visitor(output{}, v);
}
因为 boost::apply_visitor() 确保了编译时代码的正确性,所以应该优先于 boost::get()。
到此这篇关于C++ Boost Variant示例超详细讲解的文章就介绍到这了,更多相关C++ Boost Variant内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!