C++ Boost Variant示例超详细讲解
无水先生 人气:0一、提要
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()。
加载全部内容