Guia de Desenvolvimento Boost-4.4atribuir

atribuir

Em muitos casos, precisamos inicializar ou atribuir valores ao contêiner e preencher uma grande quantidade de dados, como códigos de erro iniciais e mensagens de erro, ou alguns dados de teste. No C++ 98, o contêiner padrão fornece apenas um método para acomodar esses dados, mas a etapa de preenchimento é bastante problemática. Funções de membro como insert() ou push_back() devem ser chamadas repetidamente. Esta é a razão pela qual boost.assign aparece. .

A biblioteca de atribuição sobrecarrega o operador de atribuição operator+=, o operador de vírgula operator,e o operador de colchetes operador(), que pode facilmente atribuir ou inicializar contêineres padrão com uma sintaxe inimaginavelmente concisa. Útil onde é necessário preencher um grande número de valores iniciais. O padrão C++ 11 também fornece uma lista de inicialização com funções semelhantes, mas as funções não são tão completas quanto a biblioteca de atribuição.

A biblioteca de atribuição está localizada no namespace boost::assign. Para usar a biblioteca de atribuição, você precisa incluir o arquivo de cabeçalho <boost/assign.hpp>, que contém a maioria das ferramentas da biblioteca de atribuição, a saber:

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

list_inserter

list_inserter é uma classe de ferramenta usada na biblioteca de atribuição para operar contêineres.É semelhante a std::back_inserter,mas adiciona muitas sobrecargas de operador e classes auxiliares para simplificar o código.

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 armazena internamente um objeto de função insert_ que é usado para operar o contêiner. Este objeto de função empacota push_back, push_front e outras operações do contêiner, por exemplo:

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

Outro recurso da função membro list_inserter é return *this, que permite operar em série como operações de fluxo padrão para simplificar o código.

list_inserter também fornece funções de repetição/intervalo para simplificar o trabalho de inserção de dados repetidos.

Usar operador+=

A diretiva using deve ser usada ao usar a biblioteca de atribuição. Somente desta forma o += sobrecarregado e outros operadores podem ter efeito dentro do escopo. Por exemplo:

#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");
}

O código acima demonstra a capacidade da biblioteca de atribuição de operar contêineres padrão. O operador += pode ser seguido por vários elementos que podem ser acomodados pelo contêiner, e os elementos são separados por vírgulas. Os elementos não precisam ser constantes; expressões ou chamadas de função também são aceitáveis, desde que o resultado possa ser convertido em um tipo que o contêiner possa conter. O que é especial é o contêiner do mapa. Você deve usar a função auxiliar make_pair() para gerar elementos do contêiner. Simplesmente colocar os dois membros do par entre parênteses é inválido.

A biblioteca de atribuição fornece três funções de fábrica push_back(), push_front() e insert(). Essas funções podem atuar em um contêiner com uma função membro de mesmo nome, aceitar uma instância de contêiner como parâmetro e criar um objeto list_inserter correspondente.
No namespace boost::assign, a biblioteca de atribuição sobrecarrega o operador+= para o contêiner padrão e chama a função push_back() ou insert() no formato:

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

Quando operador+= é aplicado a um contêiner, a função de fábrica push_back() é chamada para gerar um objeto list_inserter. Tomando este objeto como ponto de partida, operador() e operador subsequentes serão executados em sequência, usando push_back() ou insert( ) para inserir dados no contêiner. Como list_inserter sobrecarrega o operador vírgula raramente usado, a chamada de função é bastante simplificada.

operador+= é muito útil, mas é uma pena que a biblioteca de atribuição forneça apenas sobrecargas para contêineres padrão C++ 98 (vetor, lista, conjunto, etc.). Para operar outros tipos de contêineres (como contêineres Boost), você só pode operar de acordo com seu princípio. O princípio se realiza.

Usar operador()

operador+= só funciona em contêineres padrão e é um pouco problemático ao lidar com contêineres de mapas, então podemos usar diretamente as funções de fábrica insert()/push_front()/push_back() e usar diretamente os objetos list_inserter que eles retornam para preencher os dados.

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工厂函数
}

Este código não é muito diferente de usar o operador+=. Para contêineres (vetor, lista, deque) que possuem funções de membro push_back() ou push_front(), você pode chamar atribuír::push_back() ou atribuir::push_front(), enquanto para definir e mapear, você só pode usar atribuir: : inserir().

A vantagem do operador() é que você pode usar vários parâmetros entre parênteses, o que é muito conveniente para tipos como map cujos elementos são compostos por vários valores, evitando o uso da função make_pair(). E se não houver parâmetros entre colchetes, list_inserter chamará o construtor padrão do elemento contêiner para preencher um valor padrão, o que o operador vírgula não pode fazer.

O operador de colchetes também pode ser usado com operadores como vírgulas. O método de escrita é mais simples e às vezes nem parece um código C++ legal (mas é de fato um código C++ completamente correto), por exemplo:

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', '']

lista_genérica

list_inserter resolve o problema de atribuição ao container, mas às vezes precisamos completar o preenchimento dos dados quando o container é construído. Este método é mais eficiente que a atribuição.

O padrão C++ 11 introduz a lista de inicialização std::initializer_list, e a biblioteca boost.assign fornece uma generic_list com funções semelhantes. Seu resumo de classe é o seguinte:

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;
};

Semelhante ao list_inserter, generic_list também sobrecarrega os operadores de vírgula e colchetes.Porque precisa interoperar com o contêiner durante a inicialização, ele também adiciona algumas funções de operação do contêiner.

generic_list usa internamente std::deque para armazenar elementos, e a maioria das operações são convertidas para push_back() do deque, por exemplo:

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

Inicializar contêiner

A biblioteca de atribuição fornece três funções de fábrica list_of(), map_list_of()/pair_list_of() e tuple_list_of(), que podem gerar objetos generic_list. Então podemos usar operator() e operadores como list_inserter para preencher os dados.

Como generic_list fornece operações de conversão implícitas para tipos de contêiner, ele pode ser atribuído a qualquer contêiner.Claro, também podemos chamar explicitamente a função de conversão de contêiner.

A declaração da função list_of() é:

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

Seu uso é semelhante ao insert(), push_back() e outras funções anteriores:

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”)]
}

A função list_of() pode usar todos os operadores de parênteses ou combinar parênteses com vírgulas, mas ao usar o último, você precisa colocar toda a expressão list_of entre parênteses, caso contrário o compilador não será capaz de deduzir o tipo de list_of e não poderá atribuir um valor. .

O código padrão C++ 11 correspondente ao código acima é

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"}};

Comparando os dois trechos de código, embora a biblioteca de atribuição seja um pouco mais problemática, ela é melhor em termos de compatibilidade com C++98 e C++11.

Não é muito conveniente usar list_of() para processar contêineres de mapas, então map_list_of()/pair_list_of() surgiu. map_list_of() pode aceitar dois parâmetros e, em seguida, constrói automaticamente um objeto std::pair e o insere no contêiner de mapa.pair_list_of() é puramente sinônimo de map_list_of, o uso e as funções dos dois são exatamente os mesmos.

As formas básicas de map_list_of() e pair_list_of() são as seguintes:

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);
}

O código a seguir demonstra o uso de 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")]

Reduza entradas duplicadas

Ao preencher os dados, você encontrará o problema de inserir dados repetidos.Se você usar o método anterior para escrever muitos códigos repetidos, será muito problemático e poderá facilmente levar a erros de substituição ou subscrição. Tanto list_inserter quanto generic_list fornecem funções de membro repetir(), repetir_fun() e range() para reduzir a carga de trabalho.

Uma breve declaração dessas três funções é a seguinte:

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);

A função repeat() toma o segundo parâmetro como o valor a ser preenchido e repete o número de vezes especificado pelo primeiro parâmetro, o que é muito semelhante ao construtor de containers como vectorvdeque; a função repeat_fun() também repete o primeiro parâmetro o número de vezes, mas os dois parâmetros são uma função ou objeto de função sem parâmetros, que retorna o valor preenchido; a função range() pode inserir todos ou parte dos elementos de uma sequência em outra sequência.

Eles também retornam listas list_inserter ou generic_list, para que possam ser concatenados em operador() e operador, sequências de operação.

O código que demonstra seu uso é o seguinte:

#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;
}

Trabalhando com contêineres fora do padrão

A biblioteca de atribuição não apenas oferece suporte a todos os oito contêineres padrão (vetor, string, deque, lista, conjunto, multiset, mapa, multimapa), mas também fornece suporte apropriado para adaptadores de contêiner, incluindo pilha, fila e prioridade_queue.

Como esses três adaptadores de contêiner não possuem funções push_back/push_front, a biblioteca de atribuição fornece a função push() para atribuição, mas a pilha pode usar operador+=. A expressão list_of inicializada finalmente usa a função de membro to_adapter() para se adaptar ao contêiner não padrão. Se você usar o operador vírgula, será necessário colocar toda a expressão entre parênteses antes de usar o operador ponto para chamar to_adapter().

O código que demonstra como a biblioteca de atribuição é aplicada a um adaptador de contêiner é o seguinte:

#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();
	}

}

Outros tópicos

A biblioteca de atribuição também possui duas funções com funções semelhantes, ref_list_of() e cref_list_of(). Essas duas funções aceitam referências de variáveis ​​​​como parâmetros para criar listas de inicialização. Elas são mais eficientes que o deque interno de list_of(), mas seu uso é ligeiramente problemático, por exemplo:

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);

exemplo de código

#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();
}

Insira a descrição da imagem aqui

Acho que você gosta

Origin blog.csdn.net/qq_36314864/article/details/132272546
Recomendado
Clasificación