Boost实用工具之如何简单、高效初始化assign

12 篇文章 5 订阅

1 如何给容器初始化、赋值大量数据?

这个问题,其实解决办法很普通。
比如,重复调用push_back()、insert()等函数,或者使用初值列语法。
但是我们希望有更高效的大量数据初始化的方法

assign下有两个重要的类,分别负责不同的场景

  1. list_inserter容器赋值(先创建空的,再赋值)
  2. generic_list容器构造时填充数据

对比上面两种方案,无疑第二种的效率更高,因为它构造对象的时候就已经填充了数据,而第一种先创建一个空的,再对这个赋值。

2 list_inserter容器赋值(先创建空的,再赋值)

下面时list_inserter部分函数,更多函数可以查看源码

    template< class Function, class Argument = assign_detail::forward_n_arguments > 
    class list_inserter {
    public:
    	list_inserter& operator,(T&& r);
    	list_inserter& operator()();
    	list_inserter& operator()(const T&);
		
		list_inserter& repeat_fun( std::size_t sz, Function r );
		list_inserter& repeat( std::size_t sz, T r )
    	
    	list_inserter& range( SinglePassIterator first, SinglePassIterator last );
    	list_inserter& range( const SinglePassRange& r );
	private:
		Function insert_;	// 其实是一个函数对象
    }

这里我们可以看出一个特点,
返回类型都是自己的引用<—>list_inserter& ,assign库中的两个类都是这样的
那为什么返回自己的引用
答:好比cout << ... << ... << ,<<符号可以一个接一个写下去,返回自己的引用—运行完当前又可以继续调用当前类中函数,反复下去

ostream& print(ostream& os, int xx){
	// ...
	return os
}

这些函数的返回值都是return *this;,来保证获得自己的引用,总体来说,很像迭代器,处理完毕,指向末尾,如果操作,继续在末尾继续操作,直到所有操作执行完毕。

	template< class T >
	list_inserter& operator,(T&& r)
	{
		// do something
	    return *this;
	}

在正式分析前,我们看看容器的种类,这样你才能理解assign库的兼容性。

  1. 容器有push_back,push_front函数,如vector,string,list,deque等
  2. 容器没有push_back,push_front函数,但是有insert,如set、map等
  3. 既没有push_back,push_front函数,也没有insert,如非标准容器,如stack、queue、priority_queue

不管什么容器,assign都给出了不错的解决方案

2.1 operator,

重载,运算符,不过我也是第一次遇见重载,。这为简化书写提供了有力的帮助

        list_inserter& operator,( const T& r )
        {
            insert_( r  );
            return *this;
        }

很简单,做一个插入的操作,返回自己的引用。

2.2 operator+=

+=和,运算符保证下面的代码的正确、简单性!

	// v = [1,2,3,4,5,36]
    std::vector<int> v;
    v += 1, 2, 3, 4, 5, 6 * 6;

看看+=的源代码:

    template< class V, class A, class V2 >
    inline list_inserter< assign_detail::call_push_back< std::vector<V, A> >, V >
    operator+=( std::vector<V, A>& c, V2&& v )
    {
        return push_back( c )( std::forward<V2>(v) );
    }

这个函数调用内部的push_back函数,(push_back返回list_inserter对象,当然push_front同理,前提容器支持这种操作,这就是上面对容器分类的原因),下面是具体细节

  1. push_back( c )返回值是list_inserter,产生一个list_inserter对象;注意c是容器,由容器构建list_inserter对象!!!
  2. 借此调用operator()(Ts&&… ts),此时完成1的插入,
  3. 借此调用operator,(T&&),此时完成2的插入,直到6*6
  4. 最后+=返回list_inserter对象

push_back( c )返回值是list_inserter,继续调用operator()(Ts&&… ts),这就是要返回自己或者自己的引用的原因,表面上看着是两个()。
和上面不同的是,其实内部的push_back、push_front、insert、返回的是list_inserter对象,和上面有些不同。

2.3 不同容器的不同初始化方法

2.3.1 容器有push_back,push_front函数

下面的两种方法都是可以的,回顾上面,其实+=内部就是调用:push_back(v)(...)。因此我推荐第二种方式,看着简单方便!

// Method1 
        std::vector<int> v;
        boost::assign::push_back(v)(1)(2)(3)(4)(5)(6*6);
// Method2
	    std::vector<int> v;
    	v += 1, 2, 3, 4, 5, 6 * 6;
// list等其他容器也是这样的

2.3.2 容器没有push_back,push_front函数,但是有insert

有两种方案,

  1. 运用+=,但是必须使用make_pair(map的哈)
  2. 使用insert进行插入
// Method1 
    std::map<int, std::string> m;
    m += std::make_pair(1, "one"), std::make_pair(2, "two");
    //注意:set只有一个元素,所有就不用make_pair了哈
    	    std::set<std::string> s;
   			s += "c", "cpp", "h", "hpp";
// Method2
    // 如果容器没有push_back,push_front,如set、map,要用insert
    std::map<int, std::string> m;
    boost::assign::insert(m)(1, "one")(2, "two");
// 其他也行,如set.map

下面是+=内部代码,可知,两种方式其实是一致的,+=调用:insert(v)(...)

    operator+=( std::map<K,V,C,A>& m, const P& p )
    {
        return insert( m )( p );
    }

总之,如果不想写make_pair,那么就用第二种方案,书写简单些

2.3.3 容器没有push_back,push_front函数,也没有insert

此时,其实对应的是非标准容器,也就是stack、queue、priority_queue。这些容器也想使用assign的一些技术,该怎么做呢?
我们首先必须注意的是,assign类有两个类,一个负责赋值,一个负责构造时填充!!!
答:generic_list---->通过内部函数to_adapter()进行转换,list_inserter—>使用+=(内部使用的push)

	// list_inserter方案
	std::stack<int> stk;
	stk += 4, 5, 6; 
		// 当然也可以调用内部push
		 boost::assign::push(stk)(7);
		
	// generic_list方案
	std::stack<int> stk2 = (boost::assign::list_of(1), 2, 3).to_adapter();

2.4 重复输入和拷贝序列

案例,如果我想给这样一组数

1 2 3 3 3 3 3 3 3 3 3 3 4 5

现有的方法是

   std::vector<int> v;
  	v += 1, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 5;

这很不明智,使用repeat来解决,repeat_fun可调用函数

// 下面的代码将10重复3次
std::vector<int> v = boost::assign::list_of(1)(2).repeat(10, 3)(4)(5);
// 当然想用,就需要加大括号
std::vector<int> v = (boost::assign::list_of(1).repeat(3, 2), 4, 5);

repeat_fun案例

	std::multiset<int> ms;
	boost::assign::insert(ms).repeat_fun(3, &rand).repeat(3, 1), 10;
	//output demo 
		// 1 1 1 10 846930886 1681692777 1804289383 

range:构造d的时候用v的部分,

	std::deque<int> d;
	push_front(d).range(v.begin(), v.begin() + 3);

3 generic_list容器构造时填充数据

generic_list适用于构造时初始化,省略了构造空的代价
generic_list和list_inserter大体一致,返回自己的引用,用法差不多。不过generic_list内部用std::deque来存储元素,大部分上层函数在底部都转换成deque的push_back

typedef std::deque<Ty>  impl_type;

为了产生generic_list对象,assign库提供了三个特殊函数,list_of、map_list_of/pair_list_of、tuple_list_of

3.1 list_of

当然,这个函数的基础任是operator,、operator()等,自己看generic_list的源码

有下面两种方案,如果像偷懒用,的话,使用第二种,注意语法格式

  1. 整体大括号
  2. 第一个数值括号,其他的用,就可以了,当然用括号也可以(也就是2 3 4 5用括号和,都可以)
// v = [1,2,3,4,5]
// 第一种
std::vector<int> v = boost::assign::list_of(1)(2)(3)(4)(5);
// 第二种
std::vector<int> v = (boost::assign::list_of(1), 2, 3, 4, 5);

在C++语法中,用初值列也可以办到

std::vector<int> v = {12345};

感觉这种含简单些?是的,但是assign具有强大的兼容性,这是它最大的优势。

3.2 map_list_of/pair_list_of

运用list_of在处理map时候很复杂,这时就用map_list_of/pair_list_of,map_list_of和pair_list_of完全等价

	// 等价
	// list_of配合make_pair一起使用
	std::map<int, std::string> m = boost::assign::list_of(std::make_pair(1, "one"))(std::make_pair(2, "two"));
	
	// 使用pair_list_of
	std::map<int, std::string> m1 = boost::assign::pair_list_of(1, "one")(2, "two");

总之,既然库提供了更加简便的方法,那就用呗。

3.3 tuple_list_of

tuple是三元组,类似

3.4 重复输入和拷贝序列,和list_inserter类似不重复

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值