2010-03-23 9 views
10

Si tengo una clase que deseo poder ordenar (es decir, soporte un concepto menor que), y tiene varios elementos de datos tales que necesito hacer un ordenamiento lexicográfico, entonces necesito algo como esto:¿Cuál es la forma más simple de definir la comparación lexicográfica para los elementos de una clase?

struct MyData { 
    string surname; 
    string forename; 

    bool operator<(const MyData& other) const { 
    return surname < other.surname || (surname==other.surname && forename < other.forename); } 
}; 

Esto se vuelve bastante inmanejable para cualquier cosa con más de 2 miembros de datos. ¿Hay formas más simples de lograrlo? Los miembros de datos pueden ser cualquier clase Comparable.

Respuesta

4

Con el advenimiento de C++ 11 hay una manera nueva y concisa para lograr esto usando std::tie:

bool operator<(const MyData& other) const { 
    return std::tie(surname, forename) < std::tie(other.surname, other.forename); 
} 
2

Puede usar un boost::tuple o std::pair que tiene una comparación lexigráfica incorporada. Por supuesto, la desventaja es que no se puede asociar un método a las tuplas.

5

Se puede almacenar los datos en un boost::tuple, que proporciona la comparación lexicográfica, y proporcionar funciones de acceso con nombre, a lo largo de las líneas de:

#include <boost/tuple/tuple.hpp> 
#include <boost/tuple/tuple_comparison.hpp> 

struct Data { 
    string &surname() {return stuff.get<0>();} 
    string &forename() {return stuff.get<1>();} 

    // it would be polite to add const overloads too. 

    bool operator<(const Data &other) const {return stuff < other.stuff;} 

private: 
    boost::tuple<string, string> stuff; 
}; 

Creo que esto también está disponible como std::tr1::tuple, y será std::tuple en el próximo estándar.

Mantener la lista de accesadores es probablemente más manejable que mantener el código de comparación.

+1

solución interesante. Parece una pena perder los campos con nombre, ya que ver los contenidos de la clase en el depurador se vuelve más difícil. –

+4

Puede mantener los campos con nombre y usar la tupla solo para fines de comparación, por ejemplo, 'return boost :: tie (apellido, nombre) UncleBens

+2

Aún mejor, wrap ' tie' en una función miembro. Entonces solo tienes que mantener una sola lista del orden lexicográfico. –

3

Si todos los miembros tienen el mismo tipo, puede ponerlos en std::vector. Por defecto, std::lexicographical_compare se usará para comparar vectores.

9

tuple es una buena idea, pero si quiere seguir teniendo los nombres de sus variables miembro, podría ser lo suficientemente bueno para reestructurar su función de comparación de esta manera:

struct MyData { 
    string surname; 
    string forename; 
    string var; 
    // ... 

    bool operator<(const MyData& other) const { 
     if (surname != other.surname) return surname < other.surname; 
     if (forename != other.forename) return forename < other.forename; 
     if (var != other.var) return var < other.var; 

     // ... 

     return false; //< They are equal 
    } 
}; 

Dependiendo de su gusto, es posible que incluso quiere una macro como #define COMPARE(field) if (field != other.field) return field < other.field; para reducir la duplicación. Entonces la función simplemente se convertiría en una lista de COMPARE -invocaciones.

+0

Quizás '#define LEX_LT (mem) if (mem! = Other.mem) devuelva mem kennytm

+0

Me gusta el diseño de eso, es mucho más manejable y menos propenso a errores que el anidado-if. No soy reacio al uso ocasional de macros, y eso podría ser útil para estructuras más grandes, pero creo que la forma básica es lo suficientemente clara. –

Cuestiones relacionadas