Como se classifica um vetor que contém objetos personalizados (isto é, definidos pelo usuário).
Provavelmente, padrão algoritmo STL tipo juntamente com um predicado (uma função ou um objeto de função), que operaria em um dos campos (como uma chave para a classificação) no objeto personalizado deve ser usado.
Estou no caminho certo?
248
Respostas:
Um exemplo simples usando
std::sort
Edit: Como Kirill V. Lyadvinsky apontou, em vez de fornecer um predicado de classificação, você pode implementar o
operator<
paraMyStruct
:O uso desse método significa que você pode simplesmente classificar o vetor da seguinte maneira:
Edit2: Como Kappa sugere, você também pode classificar o vetor na ordem decrescente sobrecarregando um
>
operador e alterando um pouco a chamada de classificação:E você deve chamar classificar como:
fonte
std::sort(vec.begin(), vec.end(), greater<MyStruct>())
que é limpo e elegante.#include <functional>
usar "std :: maior".operator<
e usar umstd::sort(vec.begin(), vec.end());
oustd::sort(vec.rbegin(), vec.rend());
dependendo de se deseja ter ordem crescente ou decrescente.No interesse da cobertura. Eu propus uma implementação usando expressões lambda .
C ++ 11
C ++ 14
fonte
>
vez de<
para obter a ordem decrescente.Você pode usar o functor como terceiro argumento de
std::sort
ou definiroperator<
em sua classe.fonte
const
no final da assinatura da função?const
.const
palavra-chave no final da assinatura especifica que aoperator()
função não altera a instância daXgreater
estrutura (que em geral poderia ter variáveis de membro), enquanto a indicaçãoconst
para os valores de entrada especifica apenas que esses valores de entrada são imutáveis.A classificação de um
vector
ou qualquer outro intervalo aplicável (iterador de entrada mutável) de objetos personalizados do tipoX
pode ser alcançado usando vários métodos, especialmente incluindo o uso de algoritmos de biblioteca padrão, comosort
,stable_sort
,partial_sort
oupartial_sort_copy
.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:
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)?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ãoDeixe
std::vector<X> vec_X;
estd::vector<Y> vec_Y;
1. Sobrecarregue
T::operator<(T)
ouoperator<(T, T)
use modelos de biblioteca padrão que não esperam uma função de comparação.Qualquer membro de sobrecarga
operator<
:ou grátis
operator<
:2. Use um ponteiro de função com uma função de comparação personalizada como parâmetro da função de classificação.
3. Crie uma
bool operator()(T, T)
sobrecarga para um tipo personalizado que pode ser passado como função de comparação.Essas definições de objeto de função podem ser escritas um pouco mais genéricas usando C ++ 11 e modelos:
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.
Onde o C ++ 14 permite uma expressão lambda ainda mais genérica:
que pode ser envolvido em uma macro
simplificando a criação de comparadores comuns:
fonte
bool X_less(X const &l, X const &r) const { return l.i < r.i; }
para o comparador, mas asconst
palavras-chave devem ser removidas (pois não é uma função de membro).std::sort
ou semelhante, mas precisava de uma instância deCompare
, por exemplo, ao instanciar umstd::set
?template<class T, class C> std::set<T, C> make_set(C const& compare) { return std::set<T, C>{ compare }; }
que poderia ser usado comoauto xset = make_set<X>([](auto && l, auto && r) { return l.i < r.i; });
.Você está no caminho certo.
std::sort
usaráoperator<
como função de comparação por padrão. Portanto, para classificar seus objetos, você precisará sobrecarregarbool operator<( const T&, const T& )
ou fornecer um functor que faça a comparação, assim:A vantagem do uso de um functor é que você pode usar uma função com acesso aos membros privados da classe.
fonte
operator<
um membro da classe (ou estrutura), porque um global poderia usar membros protegidos ou privados. Ou você deve torná-lo um amigo de struct C.Fiquei curioso para saber se há algum impacto mensurável no desempenho entre as várias maneiras de chamar std :: sort, então criei este teste simples:
O que ele faz é criar um vetor aleatório e, em seguida, mede quanto tempo é necessário para copiá-lo e classificá-lo (e calcular alguma soma de verificação para evitar a eliminação muito vigorosa de código morto).
Eu estava compilando com g ++ (GCC) 7.2.1 20170829 (Red Hat 7.2.1-1)
Aqui estão os resultados:
Parece que todas as opções, exceto a passagem do ponteiro de função, são muito semelhantes e a passagem de um ponteiro de função causa + 30% de penalidade.
Também parece que o operador <versão é ~ 1% mais lento (repeti o teste várias vezes e o efeito persiste), o que é um pouco estranho, pois sugere que o código gerado é diferente (não tenho habilidade para analisar --salvar- saída de temperatura).
fonte
Sim,
std::sort()
com o terceiro parâmetro (função ou objeto) seria mais fácil. Um exemplo: http://www.cplusplus.com/reference/algorithm/sort/fonte
Na sua turma, você pode sobrecarregar o operador "<".
fonte
Abaixo está o código usando lambdas
fonte
fonte
Você pode usar a classe comparadora definida pelo usuário.
fonte
Para classificar um vetor, você pode usar o algoritmo sort ().
O terceiro parâmetro usado pode ser maior ou menor ou qualquer função ou objeto também pode ser usado. No entanto, o operador padrão é <se você deixar o terceiro parâmetro vazio.
fonte
se a comparação for falsa, fará "troca".
fonte