C++ Boost Fusion创建异构容器详解
作者:无水先生
一、说明
标准库提供了许多容器,它们有一个共同点:它们是同类的。也就是说,标准库中的容器只能存储一种类型的元素。 std::vector<int> 类型的向量只能存储 int 值,而 std::vector<std::string> 类型的向量只能存储字符串。
Boost.Fusion 使创建异构容器成为可能。例如,您可以创建一个向量,其第一个元素是 int,第二个元素是字符串。此外,Boost.Fusion 提供了处理异构容器的算法。您可以将 Boost.Fusion 视为异构容器的标准库。
严格来说,从C++11开始,标准库就提供了一个异构容器,std::tuple。您可以对存储在元组中的值使用不同的类型。 Boost.Fusion 中的 boost:fusion::tuple 是类似的类型。虽然标准库没有提供更多,但元组只是 Boost.Fusion 的起点。
二、示例和代码
示例 50.1。处理融合元组
#include <boost/fusion/tuple.hpp> #include <string> #include <iostream> using namespace boost::fusion; int main() { typedef tuple<int, std::string, bool, double> tuple_type; tuple_type t{10, "Boost", true, 3.14}; std::cout << get<0>(t) << '\n'; std::cout << get<1>(t) << '\n'; std::cout << std::boolalpha << get<2>(t) << '\n'; std::cout << get<3>(t) << '\n'; }
示例 50.1 定义了一个由 int、std::string、bool 和 double 组成的元组。该元组基于 boost:fusion::tuple。在示例 50.1 中,元组随后被实例化、初始化,并使用 boost::fusion::get() 检索各种元素并写入标准输出。函数 boost::fusion::get() 类似于 std::get(),它访问 std::tuple 中的元素。
融合元组与标准库中的元组没有区别。因此,Boost.Fusion 提供函数 boost::fusion::make_tuple() 就不足为奇了,它的工作方式类似于 std::make_tuple()。但是,Boost.Fusion 提供了超出标准库中所提供功能的附加功能。
示例 50.2。使用 boost::fusion::for_each() 迭代元组
#include <boost/fusion/tuple.hpp> #include <boost/fusion/algorithm.hpp> #include <string> #include <iostream> using namespace boost::fusion; struct print { template <typename T> void operator()(const T &t) const { std::cout << std::boolalpha << t << '\n'; } }; int main() { typedef tuple<int, std::string, bool, double> tuple_type; tuple_type t{10, "Boost", true, 3.14}; for_each(t, print{}); }
示例 50.2 介绍了算法 boost::fusion::for_each(),它迭代 Fusion 容器。此处使用该函数将元组 t 中的值写入标准输出。
boost::fusion::for_each() 旨在像 std::for_each() 一样工作。 std::for_each() 仅迭代同类容器,而 boost::fusion::for_each() 适用于异构容器。您将容器而不是迭代器传递给 boost::fusion::for_each()。如果您不想遍历容器中的所有元素,可以使用视图。
示例 50.3。使用 boost::fusion::filter_view 过滤 Fusion 容器
#include <boost/fusion/tuple.hpp> #include <boost/fusion/view.hpp> #include <boost/fusion/algorithm.hpp> #include <boost/type_traits.hpp> #include <boost/mpl/arg.hpp> #include <string> #include <iostream> using namespace boost::fusion; struct print { template <typename T> void operator()(const T &t) const { std::cout << std::boolalpha << t << '\n'; } }; int main() { typedef tuple<int, std::string, bool, double> tuple_type; tuple_type t{10, "Boost", true, 3.14}; filter_view<tuple_type, boost::is_integral<boost::mpl::arg<1>>> v{t}; for_each(v, print{}); }
Boost.Fusion 提供了视图,它像容器一样工作但不存储数据。使用视图,可以以不同方式访问容器中的数据。视图类似于来自 Boost.Range 的适配器。然而,虽然来自 Boost.Range 的适配器只能应用于一个容器,但来自 Boost.Fusion 的视图可以跨越来自多个容器的数据。
示例 50.3 使用类 boost::fusion::filter_view 来过滤元组 t。过滤器指示 boost::fusion::for_each() 仅写入基于整数类型的元素。
boost::fusion::filter_view 期望第一个模板参数是要过滤的容器类型。第二个模板参数必须是过滤元素的谓词。谓词必须根据元素的类型过滤元素。
该库之所以称为 Boost.Fusion,是因为它结合了两个世界:C++ 程序在运行时处理值,在编译时处理类型。对于开发人员来说,运行时的值通常更为重要。来自标准库的大多数工具在运行时处理值。为了在编译时处理类型,使用了模板元编程。值在运行时根据其他值进行处理,而类型在编译时根据其他类型进行处理。 Boost.Fusion 允许您根据类型处理值。
传递给 boost::fusion::filter_view 的第二个模板参数是一个谓词,它将应用于元组中的每个类型。谓词需要一个类型作为参数,如果该类型应该是视图的一部分,则返回 true。如果返回 false,则过滤掉该类型。
示例 50.3 使用来自 Boost.TypeTraits 的类 boost::is_integral。 boost::is_integral 是一个检查类型是否为整数的模板。因为必须将模板参数传递给 boost::fusion::filter_view,所以使用来自 Boost.MPL 的占位符 boost::mpl::arg<1> 来创建 lambda 函数。 boost::mpl::arg<1> 类似于来自 Boost.Phoenix 的 boost::phoenix::place_holders::arg1。在示例 50.3 中,视图 v 将仅包含元组中的 int 和 bool 元素,因此,该示例会将 10 和 true 写入标准输出。
示例 50.4。使用迭代器访问 Fusion 容器中的元素
#include <boost/fusion/tuple.hpp> #include <boost/fusion/iterator.hpp> #include <boost/mpl/int.hpp> #include <string> #include <iostream> using namespace boost::fusion; int main() { typedef tuple<int, std::string, bool, double> tuple_type; tuple_type t{10, "Boost", true, 3.14}; auto it = begin(t); std::cout << *it << '\n'; auto it2 = advance<boost::mpl::int_<2>>(it); std::cout << std::boolalpha << *it2 << '\n'; }
在看过 boost::fusion::tuple 和 boost::fusion::for_each() 之后,在示例 50.4 中找到迭代器应该不足为奇。 Boost.Fusion 提供了几个独立的函数,例如 boost::fusion::begin() 和 boost::fusion::advance(),它们的工作方式类似于标准库中的同名函数。
迭代器要递增的步数作为模板参数传递给 boost::fusion::advance()。该示例再次使用来自 Boost.MPL 的 boost::mpl::int_。
boost::fusion::advance() 返回一个与传递给函数的迭代器类型不同的迭代器。这就是示例 50.4 使用第二个迭代器 it2 的原因。您不能将 boost::fusion::advance() 的返回值分配给第一个迭代器 it。示例 50.4 将 10 和 true 写入标准输出。
除了示例中介绍的函数之外,Boost.Fusion 还提供了与迭代器一起使用的其他函数。其中包括:boost::fusion::end()、boost::fusion::distance()、boost::fusion::next() 和 boost::fusion::prior()。
示例 50.5。具有 boost::fusion::vector 的异构向量
#include <boost/fusion/container.hpp> #include <boost/fusion/sequence.hpp> #include <boost/mpl/int.hpp> #include <string> #include <iostream> using namespace boost::fusion; int main() { typedef vector<int, std::string, bool, double> vector_type; vector_type v{10, "Boost", true, 3.14}; std::cout << at<boost::mpl::int_<0>>(v) << '\n'; auto v2 = push_back(v, 'X'); std::cout << size(v) << '\n'; std::cout << size(v2) << '\n'; std::cout << back(v2) << '\n'; }
到目前为止,我们只看到了一个异构容器,boost::fusion::tuple。示例 50.5 引入了另一个容器,boost::fusion::vector。
boost::fusion::vector 是一个向量:通过索引访问元素。访问不是使用运算符 operator[] 实现的。相反,它是使用独立函数 boost::fusion::at() 实现的。索引作为用 boost::mpl::int_ 包装的模板参数传递。
此示例将一个 char 类型的新元素添加到向量中。这是通过独立函数 boost::fusion::push_back() 完成的。两个参数被传递给 boost::fusion::push_back():要添加元素的向量和要添加的值。
boost::fusion::push_back() 返回一个新向量。矢量 v 没有改变。新向量是添加了元素的原始向量的副本。
此示例使用 boost::fusion::size() 获取向量 v 和 v2 中的元素数量,并将这两个值写入标准输出。该程序显示 4 和 5。然后调用 boost::fusion::back() 获取 v2 中的最后一个元素并将其写入标准输出,在本例中值为 X。
如果您更仔细地查看示例 50.5,您会注意到 boost::fusion::tuple 和 boost::fusion::vector 之间没有区别;他们是一样的。因此,示例 50.5 也可以与 boost::fusion::tuple 一起使用。
Boost.Fusion 提供了额外的异构容器,包括:boost::fusion::deque、boost::fusion::list 和 boost::fusion::set。示例 50.6 引入了 boost::fusion::map,它是键/值对的容器。
示例 50.6。带有 boost::fusion::map 的异构映射
#include <boost/fusion/container.hpp> #include <boost/fusion/sequence.hpp> #include <boost/fusion/algorithm.hpp> #include <string> #include <iostream> using namespace boost::fusion; int main() { auto m = make_map<int, std::string, bool, double>("Boost", 10, 3.14, true); if (has_key<std::string>(m)) std::cout << at_key<std::string>(m) << '\n'; auto m2 = erase_key<std::string>(m); auto m3 = push_back(m2, make_pair<float>('X')); std::cout << std::boolalpha << has_key<std::string>(m3) << '\n'; }
示例 50.6 使用 boost::fusion::map() 创建了一个异构映射。地图的类型是 boost::fusion::map,由于关键字 auto,它没有在示例中写出。
boost::fusion::map 类型的映射像 std::map 一样存储键/值对。但是,Fusion 映射中的键是类型。键/值对由一个类型和一个映射到该类型的值组成。该值可能是与键不同的类型。在示例 50.6 中,字符串“Boost”映射到键 int。
创建映射后,调用 boost::fusion::has_key() 以检查键 std::string 是否存在。然后,调用 boost::fusion::at_key() 以获取映射到该键的值。因为数字 10 映射到 std::string,所以它被写入标准输出。
然后使用 boost::fusion::erase_key() 擦除键/值对。这不会改变地图 m。 boost::fusion::erase_key() 返回一个新映射,该映射缺少已擦除的键/值对。
对 boost::fusion::push_back() 的调用将一个新的键/值对添加到映射中。键是浮点数,值是“X”。调用 boost::fusion::make_pair() 来创建新的键/值对。此函数类似于 std::make_pair()。
最后,再次调用 boost::fusion::has_key() 以检查映射是否具有键 std::string。因为它已被删除,所以返回 false。
请注意,在调用 boost::fusion::at_key() 之前,您不需要调用 boost::fusion::has_key() 来检查密钥是否存在。如果传递给 boost::fusion::at_key() 的键在映射中不存在,则会出现编译器错误。
示例 50.7。结构融合适配器
#include <boost/fusion/adapted.hpp> #include <boost/fusion/sequence.hpp> #include <boost/mpl/int.hpp> #include <iostream> struct strct { int i; double d; }; BOOST_FUSION_ADAPT_STRUCT(strct, (int, i) (double, d) ) using namespace boost::fusion; int main() { strct s = {10, 3.14}; std::cout << at<boost::mpl::int_<0>>(s) << '\n'; std::cout << back(s) << '\n'; }
Boost.Fusion 提供了几个宏,让您可以将结构用作 Fusion 容器。这是可能的,因为结构可以充当异构容器。由于宏 BOOST_FUSION_ADAPT_STRUCT,示例 50.7 定义了一个可以用作 Fusion 容器的结构。这使得可以使用具有 boost::fusion::at() 或 boost::fusion::back() 等函数的结构。
示例 50.8。对 std::pair 的融合支持
#include <boost/fusion/adapted.hpp> #include <boost/fusion/sequence.hpp> #include <boost/mpl/int.hpp> #include <utility> #include <iostream> using namespace boost::fusion; int main() { auto p = std::make_pair(10, 3.14); std::cout << at<boost::mpl::int_<0>>(p) << '\n'; std::cout << back(p) << '\n'; }
Boost.Fusion 无需使用宏即可支持 std::pair 和 boost::tuple 等结构。您只需包含头文件 boost/fusion/adapted.hpp(参见示例 50.8)。
#include <boost/math/constants/constants.hpp> #include <iostream> struct animal { std::string name; int legs; bool has_tail; }; struct important_numbers { const float pi = boost::math::constants::pi<float>(); const double e = boost::math::constants::e<double>(); }; template <class T> void debug(const T &t) { // TODO: Write member variables of t to standard output. } int main() { animal a{ "cat", 4, true }; debug(a); important_numbers in; debug(in); }
到此这篇关于C++ Boost Fusion创建异构容器详解的文章就介绍到这了,更多相关C++ Boost Fusion内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!