La clasificación de tal vector
o cualquier otro rango aplicable (iterador de entrada mutable) de objetos de tipo personalizados X
se puede lograr usando varios métodos, especialmente incluyendo el uso de algoritmos de biblioteca estándar como
Como la mayoría de las técnicas, para obtener un orden relativo de X
elementos, ya se han publicado, comenzaré con algunas notas sobre "por qué" y "cuándo" usar los diversos enfoques.
El "mejor" enfoque dependerá de diferentes factores:
- ¿La clasificación de rangos de
X
objetos es una tarea común o rara (se ordenarán dichos rangos en diferentes lugares del programa o por los usuarios de la biblioteca)?
- ¿La clasificación requerida es "natural" (esperada) o hay varias formas en que el tipo podría compararse a sí mismo?
- ¿El rendimiento es un problema o la clasificación de los rangos de
X
objetos debe ser infalible?
Si la ordenación de los rangos de X
es una tarea común y es de esperar la ordenación lograda (es decir, X
simplemente envuelve un único valor fundamental), entonces probablemente iría por sobrecargaoperator<
ya que permite la clasificación sin ninguna confusión (como pasar correctamente los comparadores adecuados) y produce repetidamente los resultados esperados resultados.
Si ordenar es una tarea común o es probable que se requiera en diferentes contextos, pero hay varios criterios que se pueden usar para ordenar X
objetos, elegiría Functors ( operator()
funciones sobrecargadas de clases personalizadas) o punteros de función (es decir, un functor / función para ordenamiento léxico y otro para ordenamiento natural).
Si ordena rangos de tipo X
es poco común o poco probable en otros contextos, tiendo a usar lambdas en lugar de saturar cualquier espacio de nombres con más funciones o tipos.
Esto es especialmente cierto si la clasificación no es "clara" o "natural" de alguna manera. Puede obtener fácilmente la lógica detrás del pedido cuando mira una lambda que se aplica en el lugar, mientras que operator<
es opaca a primera vista y tendría que buscar la definición para saber qué lógica de pedido se aplicará.
Sin embargo, operator<
tenga en cuenta que una sola definición es un único punto de falla, mientras que múltiples lambas son múltiples puntos de falla y requieren más precaución.
Si la definición de operator<
no está disponible donde se realiza la ordenación / se compila la plantilla de ordenación, el compilador podría verse obligado a realizar una llamada a la función al comparar objetos, en lugar de incluir la lógica de ordenación que podría ser un inconveniente grave (al menos cuando La optimización del tiempo de enlace / generación de código no se aplica).
Formas de lograr comparabilidad class X
para utilizar algoritmos de clasificación de bibliotecas estándar
Dejar std::vector<X> vec_X;
ystd::vector<Y> vec_Y;
1. Sobrecargue T::operator<(T)
o operator<(T, T)
use plantillas de biblioteca estándar que no esperen una función de comparación.
Cualquiera de los miembros 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());
o gratis 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. Utilice un puntero de función con una función de comparación personalizada como parámetro de función de clasificación.
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. Cree una bool operator()(T, T)
sobrecarga para un tipo personalizado que se puede pasar como functor de comparación.
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{});
Esas definiciones de objetos de función se pueden escribir un poco más genéricas usando C ++ 11 y plantillas:
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 se puede usar para ordenar cualquier tipo con i
soporte de miembros <
.
4. Pase un cierre anónimo (lambda) como parámetro de comparación a las funciones de clasificación.
struct X {
int i{}, j{};
};
std::sort(vec_X.begin(), vec_X.end(), [](X const &l, X const &r) { return l.i < r.i; });
Donde C ++ 14 permite una expresión lambda aún más genérica:
std::sort(a.begin(), a.end(), [](auto && l, auto && r) { return l.i < r.i; });
que podría estar envuelto en una macro
#define COMPARATOR(code) [](auto && l, auto && r) -> bool { return code ; }
haciendo que la creación de comparadores ordinarios sea bastante fluida:
// 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));