A classificação de um vector
ou qualquer outro intervalo aplicável (iterador de entrada mutável) de objetos personalizados do tipo X
pode ser alcançado usando vários métodos, especialmente incluindo o uso de algoritmos de biblioteca padrão, como
Como a maioria das técnicas, para obter a ordenação relativa dos X
elementos, já foi publicada, começarei com algumas notas sobre "por que" e "quando" para usar as várias abordagens.
A abordagem "melhor" dependerá de diferentes fatores:
- A classificação de intervalos de
X
objetos é uma tarefa comum ou rara (esses intervalos serão classificados em vários locais diferentes no programa ou pelos usuários da biblioteca)?
- A classificação necessária é "natural" (esperada) ou existem várias maneiras pelas quais o tipo pode ser comparado a si mesmo?
- O desempenho é um problema ou a classificação dos intervalos de
X
objetos deve ser infalível?
Se a classificação de intervalos X
for uma tarefa comum e a classificação alcançada for esperada (ou seja, X
apenas agrupar um único valor fundamental), provavelmente a sobrecarga será necessária, operator<
uma vez que permite a classificação sem distorção (como passar corretamente os comparadores adequados) e gera repetidamente os resultados esperados resultados.
Se a classificação é uma tarefa comum ou provavelmente requerida em contextos diferentes, mas existem vários critérios que podem ser usados para classificar X
objetos, eu usaria Functors ( operator()
funções sobrecarregadas de classes personalizadas) ou ponteiros de função (ou seja, um functor / função para pedidos lexicais e outro para pedidos naturais).
Se os intervalos de classificação do tipo X
são incomuns ou improváveis em outros contextos, costumo usar lambdas em vez de sobrecarregar qualquer espaço de nome com mais funções ou tipos.
Isso é especialmente verdadeiro se a classificação não for "clara" ou "natural" de alguma forma. Você pode facilmente obter a lógica por trás da ordem ao olhar para um lambda que é aplicado no local, enquanto que operator<
é ofensivo à primeira vista e você precisa procurar a definição para saber qual lógica de ordem será aplicada.
Observe, no entanto, que uma única operator<
definição é um único ponto de falha, enquanto várias lambas são múltiplos pontos de falha e requerem mais cautela.
Se a definição de operator<
não estiver disponível onde a classificação é feita / o modelo de classificação é compilado, o compilador pode ser forçado a fazer uma chamada de função ao comparar objetos, em vez de incluir a lógica de ordenação, o que pode ser uma desvantagem grave (pelo menos quando otimização do tempo do link / geração de código não é aplicada).
Maneiras de obter comparabilidade class X
para usar algoritmos de classificação de biblioteca padrão
Deixe std::vector<X> vec_X;
estd::vector<Y> vec_Y;
1. Sobrecarregue T::operator<(T)
ou operator<(T, T)
use modelos de biblioteca padrão que não esperam uma função de comparação.
Qualquer membro de sobrecarga operator<
:
struct X {
int i{};
bool operator<(X const &r) const { return i < r.i; }
};
// ...
std::sort(vec_X.begin(), vec_X.end());
ou grátis operator<
:
struct Y {
int j{};
};
bool operator<(Y const &l, Y const &r) { return l.j < r.j; }
// ...
std::sort(vec_Y.begin(), vec_Y.end());
2. Use um ponteiro de função com uma função de comparação personalizada como parâmetro da função de classificação.
struct X {
int i{};
};
bool X_less(X const &l, X const &r) { return l.i < r.i; }
// ...
std::sort(vec_X.begin(), vec_X.end(), &X_less);
3. Crie uma bool operator()(T, T)
sobrecarga para um tipo personalizado que pode ser passado como função de comparação.
struct X {
int i{};
int j{};
};
struct less_X_i
{
bool operator()(X const &l, X const &r) const { return l.i < r.i; }
};
struct less_X_j
{
bool operator()(X const &l, X const &r) const { return l.j < r.j; }
};
// sort by i
std::sort(vec_X.begin(), vec_X.end(), less_X_i{});
// or sort by j
std::sort(vec_X.begin(), vec_X.end(), less_X_j{});
Essas definições de objeto de função podem ser escritas um pouco mais genéricas usando C ++ 11 e modelos:
struct less_i
{
template<class T, class U>
bool operator()(T&& l, U&& r) const { return std::forward<T>(l).i < std::forward<U>(r).i; }
};
que pode ser usado para classificar qualquer tipo com i
suporte de membro <
.
4. Passe um fechamento anônimo (lambda) como parâmetro de comparação para as funções de classificação.
struct X {
int i{}, j{};
};
std::sort(vec_X.begin(), vec_X.end(), [](X const &l, X const &r) { return l.i < r.i; });
Onde o C ++ 14 permite uma expressão lambda ainda mais genérica:
std::sort(a.begin(), a.end(), [](auto && l, auto && r) { return l.i < r.i; });
que pode ser envolvido em uma macro
#define COMPARATOR(code) [](auto && l, auto && r) -> bool { return code ; }
simplificando a criação de comparadores comuns:
// sort by i
std::sort(v.begin(), v.end(), COMPARATOR(l.i < r.i));
// sort by j
std::sort(v.begin(), v.end(), COMPARATOR(l.j < r.j));