Domanda

Mi capita spesso di incontrare situazioni, soprattutto con l'ordinamento in C ++, dove mi sto paragonando una serie di campi al fine di confrontare una struttura più grande. Un esempio semplificato:

struct Car{
    Manufacturer make;
    ModelName model;
    Year year;
};

bool carLessThanComparator( const Car & car1, const Car & car2 ){
    if( car1.make < car2.make ){
        return true;
    }else if( car1.make == car2.make ){
        if( car1.model < car2.model ){
            return true;
        }else if( car1.model == car2.model ){
            if( car1.year < car2.year ){
                return true;
            }
        }
    }

    return false;
}

Il mio approccio istintivo sembra ingombrante, soprattutto per i più di 3 campi. Come si strutturare questa serie di confronti in C ++? Fare altre lingue forniscono una sintassi più succinta o elegante?

È stato utile?

Soluzione

Bene, se la funzione colpisce un ritorno nel se clausola, non c'è bisogno di un esplicito altro , in quanto avrebbe già salvato. Che può risparmiare sul "valle trattino":

bool carLessThanComparator( const Car & car1, const Car & car2 ) {
    if( car1.make < car2.make )
        return true;

    if ( car1.make != car2.make )
        return false;

    if( car1.model < car2.model )
        return true;

    if( car1.model != car2.model )
        return false;

    if( car1.year < car2.year )
        return true;

    return false;
}

Mi piace LISPish approccio corto circuito di MarkusQ pure.

Altri suggerimenti

Se questo accade molto si potrebbe mettere un modello come questo in un comune di intestazione:

template<typename T, typename A1, typename A2, typename A3>
bool
do_less_than(
        const typename T& t1,
        const typename T& t2,
        const typename A1 typename T::* a1,
        const typename A2 typename T::* a2,
        const typename A3 typename T::* a3)
{
    if ((t1.*a1) < (t2.*a1)) return true;
    if ((t1.*a1) != (t2.*a1)) return false;
    if ((t1.*a2) < (t2.*a2)) return true;
    if ((t1.*a2) != (t2.*a2)) return false;
    return (t1.*a3) < (t2.*a3);
}

Aggiungi altri modelli per un diverso numero di argomenti, come richiesto. Per ogni meno di funzione, è possibile poi fare qualcosa di simile a questo:

bool carLessThanComparator(const Car& car1, const Car& car2)
{
    return do_less_than(car1, car2, &Car::make, &Car::model, &Car::year);
}

Personalmente suggerirei SENZA l'= o == operatori come ci sembra di raccomandare qui - questo richiede gli argomenti / membri di avere sia meno e gli operatori pari solo fare un meno quindi controllare su una classe che li contiene -. utilizzando solo il meno dell'operatore è enought e vi farà risparmiare ridondanza e potenziali difetti in futuro

vi consiglio di scrivere:

bool operator<(const Car &car1, const Car &car2) 
{
    if(car1.make < car2.make)
        return true;
    if(car2.make < car1.make)
        return false;

    if(car1.model < car2.model)
        return true;
    if(car2.model < car1.model)
        return false;

    return car1.year < car2.year;
}

Lo so che è una vecchia questione, ma per i futuri visitatori: la moderna soluzione di C ++ 11 è quello di utilizzare std :: legame

struct Car{
    Manufacturer make;
    ModelName model;
    Year year;
};

bool operator<(Car const& lhs, Car const& rhs)
{
    return std::tie(lhs.make, lhs.model, lhs.year) < std::tie(rhs.make, rhs.model, rhs.year);
}

std::tie converte la struct in un std::tuple modo che i suddetti delegati operatore di confronto da std::tuple::operator<. Ciò a sua volta fa un lessicografica confronto rispetto all'ordine in cui gli elementi sono schierate in std::tie.

Il confronto lessicografico è in corto circuito nello stesso modo come nelle altre soluzioni a questa domanda. Ma è ancora abbastanza succinto per definire al volo all'interno di un'espressione lambda C ++. Per le classi con i membri di dati privati, è meglio definito all'interno della classe come la funzione friend.

bool carLessThanComparator( const Car & car1, const Car & car2 ){
    return (
      ( car1.make  < car2.make  ) or (( car1.make  == car2.make  ) and
      ( car1.model < car2.model ) or (( car1.model == car2.model ) and
      ( car1.year  < car2.year  ) 
      )));

- MarkusQ

Personalmente, mi piacerebbe override del ==, <,>, e tutti gli altri operatori bisogno. Che sarebbe ripulire il codice, non è nel confronto, ma quando si ha bisogno per fare il confronto. Per il confronto vero e proprio, vorrei scriverlo in modo simile a quello che ha detto Crashworks.

bool operator<(const Car &car1, const Car &car2) {
    if(car1.make < car2.make)
        return true;
    if(car1.make != car2.make)
        return false;
    if(car1.model < car2.model)
        return true;
    if(car1.model != car2.model)
        return false;
    return car1.year < car2.year;
}

Mi chiedevo la stessa cosa come il PO e incappato in questa domanda. Dopo aver letto le risposte che sono state ispirate dalla JANM e RnR di scrivere una funzione template lexicographicalMemberCompare che utilizza solo solo operator< sui membri confrontati. Utilizza anche boost :: tuple in modo che è possibile specificare come molti membri come si desidera. Eccolo:

#include <iostream>
#include <string>
#include <boost/tuple/tuple.hpp>

template <class T, class Cons>
struct LessThan
{
    static bool compare(const T& lhs, const T& rhs, const Cons& cons)
    {
        typedef LessThan<T, typename Cons::tail_type> NextLessThan;
        typename Cons::head_type memberPtr = cons.get_head();
        return lhs.*memberPtr < rhs.*memberPtr ?
            true :
            (rhs.*memberPtr < lhs.*memberPtr  ?
                false :
                NextLessThan::compare(lhs, rhs, cons.get_tail()));
    }
};

template <class T>
struct LessThan<T, class boost::tuples::null_type>
{
    static bool compare(const T& lhs, const T& rhs,
                        const boost::tuples::null_type& cons)
    {
        return false;
    }
};

template <class T, class Tuple>
bool lexicographicalMemberCompare(const T& lhs, const T& rhs,
                                  const Tuple& tuple)
{
    return LessThan<T, typename Tuple::inherited>::compare(lhs, rhs, tuple);
}

struct Car
{
    std::string make;
    std::string model;
    int year;
};

bool carLessThanCompare(const Car& lhs, const Car& rhs)
{
    return lexicographicalMemberCompare(lhs, rhs,
        boost::tuples::make_tuple(&Car::make, &Car::model, &Car::year));
}

int main()
{
    Car car1 = {"Ford", "F150", 2009};
    Car car2 = {"Ford", "Escort", 2009};
    std::cout << carLessThanCompare(car1, car2) << std::endl;
    std::cout << carLessThanCompare(car2, car1) << std::endl;
    return 0;
}

Spero che questo è utile a qualcuno.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top