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