boost:assign 客官°小女子只卖身不卖艺 2022-09-02 04:18 97阅读 0赞 很多情况下我们都需要为容器初始化或者赋值,填入大量的数据,比如初始化错误代码和错误信息,或者是一些测试用的数据。在C++98中标准容器仅提供了容纳这些数据的方法,但填充步骤确相对麻烦,必须重复调用`insert()`或者`push_back()`等成员函数,这正是`boost.assign`出现的理由。 `assign`库重载了赋值操作符`+=`,逗号操作符`,`和括号操作符`()`,可以用难以想象的简洁方法非常方便的堆标准容器赋值或者初始化。在需要填入大量初值的地方很有用。C++新标准也提供类似功能的初始化列表,但功能没有assign库那么完备。 assign库位于名字空间boost::assign,需要包含头文件`<boost/assign.hpp>`,它包含了大部分assign库: #include <boost/assign.hpp> using namespace boost::assign; ## list\_inserter ## `list_inserter`是assign库中用来操作容器的工具类,它类似std::back\_inserter,但增加了很多操作符重载和助手类来简化代码 ![在这里插入图片描述][watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3poaXpoZW5nZ3Vhbg_size_16_color_FFFFFF_t_70] list\_inserter内部存储一个函数对象insert\_来操作容器,这个函数对象包装了容器的push\_back、push\_front等操作,比如: list_inserter& operator, (const T& r) // 重置operator, { insert_(r); //向容器添加元素 return *this; //返回自身的引用 } list\_inserter成员函数的另一个特点是返回\*this,这使得它可以像标准流操作一样串联操作,达到简化代码的目的。 list\_inserter还提供repeat/range函数来简化输入重复数据的工作。 ## operator+= ## list\_inserter重载了操作符`+=`、`,`,因此可以像这样使用: #include <iostream> #include <boost/assign.hpp> using namespace boost::assign; int main() { std::vector<int> v; v += 1, 2, 3, 4, 5, 6, 7; std::set<std::string> s; s += "C", "C++", "JAVA", "GOLANG", "C++"; std::map<int, std::string> m; m += std::make_pair(1, "one"), std::make_pair(2, "two"); return 0; } 上面的代码示范了assign库操作标准容器的能力。`+=`蔡政府后可以接若干个可以被容器容纳的元素,元素之间用`,`分隔。元素不一定是常量,表达式或者函数调用也是可以接受的,只要其结果能转换成可以容纳的类型。比较特别的是map容器,必须使用辅助函数make\_pair()来生成容器元素。 > 实现原理 assign库提供三个工厂函数`push_back()`、`push_front()`和`insert()`。这些函数可作用于拥有同名成员函数的容器,接收容器实例作为参数,创建对应的list\_inserter对象。 在名字空间`boost:assign`里assign库为标准容器重载了`operator+=`,调用push\_back()或insert()函数,形式是: ![在这里插入图片描述][bc6943a6d36f4bd5b1dde4676122aee1.png] operator+=作用于容器时调用工厂函数push\_back(),产生一个list\_inserter对象,以这个对象作为起点,随后的operator()和operator,就会依次执行,使用push\_back()或insert()向容器插入数据。由于list\_inserter重载了很少使用的逗号操作符,所以函数的调用得到了极大的简化。 operator+=很好用,但有一点遗憾,assign库仅提供了对C++98标准容器(vector、list、set等)的重载,要操作其他类型的容器只能依据其原理自行实现。 ## operator() ## operator+=仅作用于标准容器,而且在处理map容器时也显得有些麻烦,所以我们可以直接使用工厂函数`insert()/push_front()/push_back()`,利用它们返回的list\_inserter对象来填入数据。 #include <iostream> #include <forward_list> #include <boost/assign.hpp> using namespace boost::assign; int main() { std::vector<int> v; push_back(v)(1)(1)(1)(1)(1); std::list<std::string> l; push_front(l)("c")("c++")("java")("go"); std::forward_list<std::string> f1; push_front(f1)("c")("c++")("java")("go"); std::set<std::string> s; insert(s)("c")("c")("c++")("java")("go"); std::map<int, std::string> m; insert(m)(1, "one")(2, "two"); return 0; } 这段代码和使用operator+=没有太多不同。对于拥有push\_back()或者push\_front()成员函数的容器(vector、list、deque),可以调用assign::push\_back()或者assign::push\_front(),而对于set和map,则只能使用assign::insert()。 operator()的好处是可以在括号中使用多个参数,这对于map这样的元素时由多个值组成的类型非常方便,避免了make\_pair()函数的使用。如果括号中没有参数,那么list\_inserter将调用容器元素的默认构造函数填入一个默认值,而逗号操作符则不能这样做。 括号操作符也可以与逗号等操作符混合使用: #include <iostream> #include <forward_list> #include <boost/assign.hpp> using namespace boost::assign; int main() { std::vector<int> v; push_back(v),1,2,(1), 3, 67/3, (8); std::deque<std::string> d; push_front(d)() = "one", "two", "thrww"; assert(d.size() == 5); // d = ["one", "two", "thrww", ""] return 0; } ## generic\_list ## `list_inserter`解决了对容器的赋值问题,但有时候我们需要在容器构造的时候就完成对数据的填充,这种方式较赋值更为高效。 C++11标准引入了初始化列表std::initializer\_list,而boost.assign库则提供了功能类型的generic\_list: ![在这里插入图片描述][watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3poaXpoZW5nZ3Vhbg_size_16_color_FFFFFF_t_70 1] 与list\_inserter类型,generic\_list也重载了逗号和括号操作符,因为要在初始化时与容器互操作,它还增加了一些容器操作函数。 generic\_list内部使用std::deque存储元素,大多数操作都转换为deque的push\_back(),比如: ![在这里插入图片描述][b5d4d6770dbe4a2087d60ca276f0d88e.png] ## 初始化容器 ## assign库提供了三个工厂函数`list_of()`、`map_list_of()/pair_list_of()`和`tuple_list_of`,它们能够产生generic\_list对象,然后我们就可以像list\_inserter一样使用operator()和`operator,`来填充数据。 因为generic\_list提供到容器类型的隐式转型操作,所以它可以赋值给任意容器,当然我们也可以显示调用容器转换函数。 > list\_of() 声明: ![在这里插入图片描述][ee9b46e03bb54a3eba19abccf4eff6d2.png] 用法:类似之前的insert、push\_back()等函数。 #include <iostream> #include <forward_list> #include <boost/assign.hpp> using namespace boost::assign; int main() { std::vector<int> v = list_of(1)(2)(3)(4)(5); //==> std::vector<int> v = {1, 2, 3, 4} std::vector<int> v1 = (list_of(1),(2),(3),(4),(5)); //==> std::vector<int> v = [1, 2, 3, 4] std::deque<std::string> d = (list_of("a"), ("bb"), ("cc")); //==> std::deque<std::string> d = {"a", "bb", "cc"}; std::set<int> s = (list_of(10), 20, 30, 40, 50); // ==> std::set<int> s = {10, 20, 30, 40, 50}; std::map<int, std::string> m = list_of(std::make_pair(1, "one")) (std::make_pair(2, "two")); // std::map<int, std::string> m1 = { {1, "one"}, {2, "two"}}; return 0; } list\_of()函数可以全部使用括号操作符,也可以把括号和逗号结合起来,但使用后者时需要将整个list\_of表达式用括号起来,否则编译器无法推导出list\_of的类型导致赋值失败。 > map\_list\_of()/pair\_list\_of() 使用list\_of()处理map容器不是很方便,于是就产生了 map\_list\_of()/pair\_list\_of(),map\_list\_of()可以接受两个参数,然后自动构造std::pair对象插入map容器,pair\_list\_of()则是map\_list\_of()的同义词,两种的用法功能完全相同。其基本形式如下: ![在这里插入图片描述][fd5ee106ce334e48956f74937aa37b49.png] 下面的代码演示了map\_list\_of的用法: std::map<int, int> m1 = map_list_of(1, 2)(3, 4)(5, 6); > tuple\_list\_of tuple\_list\_of用于初始化元素类型为tuple的容器。 ## 重复输入 ## 在填入容器时会遇到重复数据的问题,如果用之前的方法要写大量的重复代码,很麻烦。因此list\_inserter和generic\_list都提供成员函数repeat()、repeat\_func()和range()来减轻工作量。 ![在这里插入图片描述][33e1a0b479ba40fbbb01f30d16cb2264.png] repeat()函数把第二个参数作为要填入的值,重复第一个参数指定的次数,与vector、deque等容器的构造函数很相似;repeat\_func()函数同样重复第一个参数逇次数,但第二个参数是个无参的函数或者函数对象,它返回填入的数值;range()函数则可以把一个序列里的全部或者部分元素插入到另一个序列里面。 #include <iostream> #include <forward_list> #include <boost/assign.hpp> using namespace boost::assign; int main() { std::vector<int> v= list_of(1).repeat(3, 2)(3)(4)(5); // 1, 2, 2, 2, 3, 4, 5 std::multiset<int> ms; insert(ms).repeat_fun(5, &rand).repeat(2, 1), 10; // x, x, x, x, x, 1, 1, 10 std::deque<int> d; push_front(d).range(v.begin(), v.end() + 2); //0 0 5 4 3 2 2 2 1 for(auto i: d){ printf("%d\t", i); } return 0; } ## 操作非标准容器 ## assign库不仅支持标准容器(vector、string、deque、list、set、multiset、map、multimap),也对容器适配器提供了恰当的支持,包括stack、queue、priority\_queue。 因为这上容器适配器没有push\_back/push\_front函数,所以assign库提供push()函数来赋值,但stack可以使用operator+=。初始化的list\_of表达式最后则使用to\_adapter()成员函数适配到非标准容器。如果使用逗号表达式还需要把整个标段数用括号括起来,才能使用点号操作符调用to\_adapter() #include <iostream> #include <forward_list> #include <boost/assign.hpp> using namespace boost::assign; int main() { std::stack<int> stk = (list_of(1), 2, 3).to_adapter(); stk += 4, 5, 6; while(!stk.empty()){ std::cout << stk.top() << " "; stk.pop(); } std::cout << std::endl; std::queue<std::string> q = (list_of("china")("us")).repeat(2, "qussia").to_adapter(); push(q)("english"); while(!q.empty()){ std::cout << q.front() << " "; q.pop(); } std::cout << std::endl; return 0; } ## 其他 ## assign库还有两个功能类似的函数`ref_list_of()`和`cref_list_of()`,这两个函数接受变量的引用作为参数来创建初始化列表,较list\_of()的内部deque效率更高,但用法比较麻烦: int a = 1, b = 2, c = 3; std::vector<int> stk = (ref_list_of<3>(a)(b)(c)); [watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3poaXpoZW5nZ3Vhbg_size_16_color_FFFFFF_t_70]: /images/20220829/dc466ab6b7c046789a12a6d33981fb2f.png [bc6943a6d36f4bd5b1dde4676122aee1.png]: /images/20220829/3846ecf4745e4f7eaac68a4c9851db08.png [watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3poaXpoZW5nZ3Vhbg_size_16_color_FFFFFF_t_70 1]: /images/20220829/2a368007b7174e0fa479f43cc7c96ce1.png [b5d4d6770dbe4a2087d60ca276f0d88e.png]: /images/20220829/fdc4ec70f6414dc08cf2f1514db4f569.png [ee9b46e03bb54a3eba19abccf4eff6d2.png]: /images/20220829/a9e12a3b0cf740ca87fe19a8249c6d15.png [fd5ee106ce334e48956f74937aa37b49.png]: /images/20220829/a2142babfd49415da204cf207de363e5.png [33e1a0b479ba40fbbb01f30d16cb2264.png]: /images/20220829/ed437348c96b406bbf7f1938cd3081ae.png
还没有评论,来说两句吧...