Boost Development Guide-4.4assign

assign

In many cases, we need to initialize or assign values ​​to the container and fill in a large amount of data, such as initial error codes and error messages, or some test data. In C++98, the standard container only provides a method to accommodate this data, but the filling step is quite troublesome. Member functions such as insert() or push_back() must be called repeatedly. This is the reason why boost.assign appears. .

The assign library overloads the assignment operator operator+=, comma operator operator,and bracket operator operator(), which can easily assign or initialize standard containers with unimaginably concise syntax. Useful where a large number of initial values ​​need to be filled in. The C++11 standard also provides an initialization list with similar functions, but the functions are not as complete as the assign library.

The assign library is located in the namespace boost::assign. In order to use the assign library, you need to include the header file <boost/assign.hpp>, which contains most of the assign library tools, namely:

#include <boost/assign.hpp>
using namespace boost::tassign;

list_inserter

list_inserter is a tool class used in the assign library to operate containers. It is similar to std::back_inserter, but adds many operator overloads and helper classes to simplify the code.

template< class Eunction >class list_inserter
{
    
    
public:
   list_inserter& operator, (const T& r); //重载operator,
   list_inserter& operator()(); //重载operator()
   list_inserter& operator()(const T& r);
public:  //重复输入数据操作
   list_inserter& repeat(std::size_t sz, T r );
   list_inserter& repeat_fun(std::size_t sz, Nullary_function fun);
   list_inserter& range(SinglePassIterator first, SinglePassIterator last);
   list_inserter& range(const singlePassRange& r);
private:
   Function insert_;
};

List_inserter internally stores a function object insert_ which is used to operate the container. This function object packages the push_back, push_front and other operations of the container, for example:

list_inserter& operator,(const T& r) //重载operator,
{
    
    
     insert_(r); //向容器添加元素
     return *this; //返回自身的引用
}

Another feature of the list_inserter member function is return *this, which allows it to operate in series like standard stream operations to simplify the code.

list_inserter also provides repeat/range functions to simplify the work of entering repeated data.

Use operator+=

The using directive must be used when using the assign library. Only in this way can the overloaded += and other operators take effect within the scope. For example:

#include <boost/assign.hpp>
int main()
{
    
    
	using namespace boost::assign; //很重要,启用assign库的功能
	vector<int> v; //标准向量容器
	v += 1, 2, 3, 4, 5, 6 * 6; //用operator+=和,填入数据

	set<string> s; //标准集合容器
	s += "c", "cpp", "lua", "swift";

	map<int, string> m; //标准映射容器
	m += make_pair(1, "one"), make_pair(2, "two");
}

The above code demonstrates the assign library's ability to operate standard containers. The += operator can be followed by several elements that can be accommodated by the container, and the elements are separated by commas. The elements do not have to be constants; expressions or function calls are also acceptable, as long as the result can be converted to a type that the container can hold. What is special is the map container. You must use the auxiliary function make_pair() to generate container elements. Simply enclosing the two members of the pair in parentheses is invalid.

The assign library provides three factory functions push_back(), push_front() and insert(). These functions can act on a container with a member function of the same name, accept a container instance as a parameter, and create a corresponding list_inserter object.
In the namespace boost::assign, the assign library overloads operator+= for the standard container and calls the push_back() or insert() function in the form:

inline list_inserter operator+=(C& c, V v)
{
    
    
     return push_back(c)(v); //对于关联容器则是insert函数
}

When operator+= is applied to a container, the factory function push_back() is called to generate a list_inserter object. Taking this object as the starting point, subsequent operator() and operator will be executed in sequence, using push_back() or insert() to insert data into the container. Since list_inserter overloads the rarely used comma operator, function calling is greatly simplified.

operator+= is very useful, but it is a pity that the assign library only provides overloads for C++98 standard containers (vector, list, set, etc.). To operate other types of containers (such as Boost containers), you can only operate according to its The principle realizes itself.

Use operator()

operator+= only works on standard containers, and it is a bit troublesome when dealing with map containers, so we can directly use the factory functions insert()/push_front()/push_back() and directly use the list_inserter objects they return to fill in the data.

int main()
{
    
    
	using namespace boost::assign;

	vector<int> v; 
	push_back(v)(1)(2)(3)(4)(5); //使用puch_back工厂函数

	list<string> l;
	push_front(l)("c")("cpp")("lua")("swift"); //使用push_front工厂函数

	forward_list<string> fl; //C++11的单向链表
	push_front(l)("matrix")("reload"); //使用push__front工厂函数

	set<double> s;
	insert(s)(3.14)(0.618)(1.732); //使用insert工厂函数

	map<int, string> m;
	insert(m)(1, "one")(2, "two"); //使用insert工厂函数
}

This code is not much different from using operator+=. For containers (vector, list, deque) that have push_back() or push_front() member functions, you can call assign::push_back() or assign::push_front(), while for set and map, you can only use assign:: insert().

The advantage of operator() is that you can use multiple parameters in parentheses, which is very convenient for types such as map whose elements are composed of multiple values, avoiding the use of the make_pair() function. And if there are no parameters in the brackets, then list_inserter will call the default constructor of the container element to fill in a default value, which the comma operator cannot do.

The bracket operator can also be used with operators such as commas. The writing method is simpler, and sometimes it does not even look like legal C++ code (but it is indeed completely correct C++ code), for example:

using namespace boost::assign;

vector<int> v;
push_back(v), 1, 2, 3, 4, 5;
push_back(v)(6), 7, 64 / 8, (9), 10; //v=[1 2 3 4 5 6 7 8 9 10]

for (auto& x : v)
	cout << x << ",";
cout << endl;

deque<string> d;
push_front(d)() = "breath", "of", "the", "wild";
assert(d.size() == 5); //d=['wild', 'the', 'of', 'breath', '']

generic_list

list_inserter solves the problem of assignment to the container, but sometimes we need to complete the data filling when the container is constructed. This method is more efficient than assignment.

The C++11 standard introduces the initialization list std::initializer_list, and the boost.assign library provides a generic_list with similar functions. Its class summary is as follows:

template<class T>
class generic_list
{
    
    
public:
  iterator begin() const; //类似容器的接口
  iterator end() const;
  bool empty() const;
  size_type size() const;
  generic_list& operator,(const Ty& u); //重载operator,
  generic_list&operator()(); //重载operator()
  generic_list&operator()(const Ty& u);
public: //重复输入数据操作
  generic_list& repeat(std::size_t sz, U u);
  generic_list& repeat_fun(std::size_t sz, Nullary_function fun);
  generic_list& range(singlePassIterator first, singlePassIterator last);
  generic_list& range(const singlePassRange& r);
public: //容器转换
  operator container(const;
  Container to_container(Container& c) const;
  adapter_converter to_adapter() const;
  Adapter to_adapter(Adapter& a) const;
  Array to_array(Array& a) const;
};

Similar to list_inserter, generic_list also overloads the comma and bracket operators. Because it needs to interoperate with the container during initialization, it also adds some container operation functions.

generic_list internally uses std::deque to store elements, and most operations are converted to deque's push_back(), for example:

generic_list& operator,(const Ty& u) //重载operator,
{
    
    
     this->push_back(u); //push_back添加元素
     return *this; //返回自身的引用
}

Initialize container

The assign library provides three factory functions list_of(), map_list_of()/pair_list_of() and tuple_list_of(), which can generate generic_list objects. Then we can use operator() and operator like list_inserter to fill in the data.

Because generic_list provides implicit conversion operations to container types, it can be assigned to any container. Of course, we can also explicitly call the container conversion function.

The declaration of the list_of() function is:

inline generic_list<T> list_of();
inline generic_list<T> list_of(const T&t);

Its usage is similar to the previous insert(), push_back() and other functions:

int main()
{
    
    
	using namespace boost::assign;

	vector<int> v = list_of(1)(2)(3)(4)(5);
	// v = [1, 2, 3, 4, 5]

	deque<string> d =
		(list_of("power")("bomb"), "phazon", "suit"); //注意括号和逗号的使用
	// d = [power bomb phazon suit]

	set<int> s = (list_of(10), 20, 30, 40, 50); //注意括号和逗号的使用
	// s = {10 20 30 40 50}

	map<int, string> m = list_of(make_pair(1, "one"))(make_pair(2, "two"));
	// m = [(1, “one”) (2, “two”)]
}

The list_of() function can use all parentheses operators, or combine parentheses with commas, but when using the latter, you need to enclose the entire list_of expression in parentheses, otherwise the compiler will be unable to deduce the type of list_of and cannot assign a value. .

The C++11 standard code corresponding to the above code is

vector<int> v = {
    
    1,2,3,4,5};
deque<string> d = {
    
    "power", "bomb", "phazon", "suit"};
set<int> s = {
    
    10, 20, 30, 40, 50};
map<int, string> m = {
    
    {
    
    1, "one"},{
    
    2, "two"}};

Comparing the two pieces of code, although the assign library is slightly more troublesome, it is better in terms of compatibility with C++98 and C++11.

It is not very convenient to use list_of() to process map containers, so map_list_of()/pair_list_of() came into being. map_list_of() can accept two parameters, and then automatically constructs a std::pair object and inserts it into the map container. pair_list_of() is purely Synonyms of map_list_of, the usage and functions of the two are exactly the same.

The basic forms of map_list_of() and pair_list_of() are as follows:

template<class Key, class T>
map_lists_of(const Key& k, const T& t); //key, value

template<class F, class S>
pair_list_of(const F&f, const S&s)
{
    
    
    return map_list_of(f, s);
}

The following code demonstrates the use of map_list_of:

map<int, int> m1 = map_list_of(1, 2)(3, 4)(5, 6);
//m1 = [(1, 2)(3, 4)(5, 6)]

map<int, string> m2 = map_list_of(1, "one")(2, "two");
//m2 = [(1, "one")(2, "two")]

Reduce duplicate entries

When filling in data, you will encounter the problem of entering repeated data. If you use the previous method to write a lot of repeated code, it is very troublesome and can easily lead to errors of overwriting or underwriting. Both list_inserter and generic_list provide member functions repeat(), repeat_fun() and range() to reduce workload.

A brief declaration of these three functions is as follows:

list& repeat(std::size_t sz, U u);
list& repeat_fun(std::size_t sz, Nullary_function fun);
list& range(SinglePassIterator first, SinglePassIterator last);
list& range(const SinglePassRange& r);

The repeat() function takes the second parameter as the value to be filled in and repeats the number of times specified by the first parameter, which is very similar to the constructor of containers such as vectorvdeque; the repeat_fun() function also repeats the first parameter the number of times, but the The two parameters are a function or function object without parameters, which returns the filled-in value; the range() function can insert all or part of the elements in one sequence into another sequence.

They also return lists list_inserter or generic_list, so they can be concatenated into operator() and operator, operation sequences.

The code demonstrating their usage is as follows:

#include <boost/assign.hpp>
#include <cstdlib>                          //for rand()

void case5()
{
    
    
	using namespace boost::assign;

	vector<int> v = list_of(1).repeat(3, 2)(3)(4)(5);
	//v = 1,2,2,2,3,4,5
	for (auto& x : v)
		cout << x << ",";
	cout << endl;


	multiset<int> ms;
	insert(ms).repeat_fun(5, &rand).repeat(2, 1), 10;
	//ms = x,x,x,x,x,1,1,10
	for (auto& x : ms)
		cout << x << ",";
	cout << endl;


	deque<int> d;
	push_front(d).range(v.begin(), v.begin() + 5);
	//d = 3,2,2,2,1
	for (auto& x : d)
		cout << x << ",";
	cout << endl;
}

Working with non-standard containers

The assign library not only supports all eight standard containers (vector, string, deque, list, set, multiset, map, multimap), but also provides appropriate support for container adapters, including stack, queue, and priority_queue.

Because these three container adapters do not have push_back/push_front functions, the assign library provides the push() function for assignment, but stack can use operator+=. The initialized list_of expression finally uses the to_adapter() member function to adapt to the non-standard container. If you use the comma operator, you need to enclose the entire expression in parentheses before you can use the dot operator to call to_adapter().

The code that demonstrates how the assign library is applied to a container adapter is as follows:

#include <stack>
#include <queue>

int main()
{
    
    
	using namespace boost::assign;

	stack<int> stk = (list_of(1), 2, 3).to_adapter();
	stk += 4, 5, 6;
	for (; !stk.empty();) //输出stack的内容
	{
    
    
		cout << stk.top() << " ";
		stk.pop();
	}
	cout << endl;

	queue<string> q = (list_of("china")("us")("uk")).
		repeat(2, "russia").to_adapter();
	push(q)("germany");
	for (; !q.empty();) //输出queue的内容
	{
    
    
		cout << q.front() << " ";
		q.pop();
	}
	cout << endl;

	priority_queue<double> pq = (list_of(1.414), 1.732, 2.236).to_adapter();
	push(pq), 3.414, 2.71828;
	for (; !pq.empty();) //输出优先队列的内容
	{
    
    
		cout << pq.top() << " ";
		pq.pop();
	}

}

Other topics

The assign library also has two functions with similar functions, ref_list_of() and cref_list_of(). These two functions accept variable references as parameters to create initialization lists. They are more efficient than the internal deque of list_of(), but their usage is slightly troublesome, for example :

using namespace boost::assign;

int a = 1, b = 2, c = 3;

vector<int> v = ref_list_of<3>(a)(b)(c);
assert(v.size() == 3);

code example

#include <iostream>
using namespace std;

#include <boost/assign.hpp>
//using namespace boost::assign;

//
void case1()
{
    
    
	using namespace boost::assign;
	vector<int> v;
	v += 1, 2, 3, 4, 5, 6 * 6;

	for (auto& x : v)
		cout << x << ",";
	cout << endl;

	set<string> s;
	s += "c", "cpp", "lua", "swift";

	for (auto& x : s)
		cout << x << ",";
	cout << endl;

	map<int, string> m;
	m += make_pair(1, "one"), make_pair(2, "two");

}

//
#include <forward_list>
void case2()
{
    
    
	using namespace boost::assign;

	vector<int> v;
	push_back(v)(1)(2)(3)(4)(5);

	list<string> l;
	push_front(l)("c")("cpp")("lua")("swift");

	forward_list<string> fl;
	push_front(l)("matrix")("reload");

	set<double> s;
	insert(s)(3.14)(0.618)(1.732);

	map<int, string> m;
	insert(m)(1, "one")(2, "two");
}

//
void case3()
{
    
    
	using namespace boost::assign;

	vector<int> v;
	push_back(v), 1, 2, 3, 4, 5;
	push_back(v)(6), 7, 64 / 8, (9), 10;

	for (auto& x : v)
		cout << x << ",";
	cout << endl;

	deque<string> d;
	push_front(d)() = "breath", "of", "the", "wild";
	assert(d.size() == 5);

	for (auto& x : d)
		cout << x << ",";
	cout << endl;

}

//
void case4()
{
    
    
	using namespace boost::assign;

	vector<int> v = list_of(1)(2)(3)(4)(5);
	// v = [1, 2, 3, 4, 5]

	deque<string> d =
		(list_of("power")("bomb"), "phazon", "suit");
	// d = [power bomb phazon suit]

	set<int> s = (list_of(10), 20, 30, 40, 50);
	// s = {10 20 30 40 50}

	map<int, string> m = list_of(make_pair(1, "one"))(make_pair(2, "two"));
	// m = [(1, “one”) (2, “two”)]

	map<int, int> m1 = map_list_of(1, 2)(3, 4)(5, 6);
	//m1 = [(1, 2)(3, 4)(5, 6)]

	map<int, string> m2 = map_list_of(1, "one")(2, "two");
	//m2 = [(1, "one")(2, "two")]

}

//
#include <cstdlib>                          //for rand()

void case5()
{
    
    
	using namespace boost::assign;

	vector<int> v = list_of(1).repeat(3, 2)(3)(4)(5);
	//v = 1,2,2,2,3,4,5
	for (auto& x : v)
		cout << x << ",";
	cout << endl;


	multiset<int> ms;
	insert(ms).repeat_fun(5, &rand).repeat(2, 1), 10;
	//ms = x,x,x,x,x,1,1,10
	for (auto& x : ms)
		cout << x << ",";
	cout << endl;


	deque<int> d;
	push_front(d).range(v.begin(), v.begin() + 5);
	//d = 3,2,2,2,1
	for (auto& x : d)
		cout << x << ",";
	cout << endl;

}

//
#include <stack>
#include <queue>

void case6()
{
    
    
	using namespace boost::assign;

	stack<int> stk = (list_of(1), 2, 3).to_adapter();
	stk += 4, 5, 6;
	for (; !stk.empty();)
	{
    
    
		cout << stk.top() << " ";
		stk.pop();
	}
	cout << endl;

	queue<string> q = (list_of("china")("us")("uk")).
		repeat(2, "russia").to_adapter();
	push(q)("germany");
	for (; !q.empty();)
	{
    
    
		cout << q.front() << " ";
		q.pop();
	}
	cout << endl;

	priority_queue<double> pq = (list_of(1.414), 1.732, 2.236).to_adapter();
	push(pq), 3.414, 2.71828;
	for (; !pq.empty();)
	{
    
    
		cout << pq.top() << " ";
		pq.pop();
	}

}


//
void case7()
{
    
    
	using namespace boost::assign;

	int a = 1, b = 2, c = 3;

	vector<int> v = ref_list_of<3>(a)(b)(c);
	assert(v.size() == 3);
}

//

int main()
{
    
    
	case1();
	case2();
	case3();
	case4();
	case5();
	case6();
	case7();
}

Insert image description here

Guess you like

Origin blog.csdn.net/qq_36314864/article/details/132272546