2016-02-29 6 views
31

Devo sovraccaricare un operatore == in C++ per una classe con molti attributi.
L'operatore deve restituire true, se e solo se tutti gli attributi sono uguali. Una scorciatoia potrebbe essere utile, se questi attributi cambiano nel tempo, per evitare bug.C++ uguale (==) overload, collegamento o modo migliore per confrontare tutti gli attributi

Esiste una scorciatoia per confrontare tutti gli attributi di una classe?

+0

È possibile utilizzare memcmp se il proprio oggetto è POD o gran parte di esso POD (su quella parte) – VladimirS

+0

È possibile scrivere uno script (nel proprio editor se supportato. Le sostituzioni di espressioni regolari Vim potrebbero farlo, ad esempio) prendere una copia delle righe di dichiarazione e trasformarle in 'element == other.element &&' –

+8

@ user3545806 'memcmp' non renderà conto del riempimento, in modo che non funzioni. – Barry

risposta

40

Nessuna connessione. Dovrai elencare tutto.

Alcune fonti di errore possono essere ridotti con l'introduzione di una funzione di membro denominato tied() come:

struct Foo { 
    A a; 
    B b; 
    C c; 
    ... 

private: 
    auto tied() const { return std::tie(a, b, c, ...); } 
}; 

modo che il vostro operator== può semplicemente utilizzare tale:

bool operator==(Foo const& rhs) const { return tied() == rhs.tied(); } 

Questo consente di elencare solo tutta la vostra membri una volta Ma questo è tutto. Devi ancora elencarli effettivamente (quindi puoi ancora dimenticarne uno).


C'è una proposta (P0221R0) per creare un default operator==, ma non so se sarà avere accettato.

+0

@Downvoter Che succede? – Barry

2

L'unico modo per farlo è sfortunatamente controllare tutti gli attributi. La cosa buona è che se si combinano tutti gli assegni usando && si fermerà la valutazione dopo la prima falsa affermazione. (valutazione di cortocircuito)

Così ad es. false && (4 == 4). Il programma non valuterebbe mai la parte 4 == 4 poiché tutte le istruzioni combinate da && devono essere true per ottenere true come risultato finale. Ha senso ciò?

+0

Si chiama valutazione di cortocircuito. Non c'è davvero altra alternativa ad esso. – erip

+0

Inoltre, concatenato non è la parola giusta qui. – erip

+0

@erip mi dispiace. Non sono madrelingua. Quale sarebbe una parola migliore in questa situazione? – muXXmit2X

16

A partire da C++ 11 con l'introduzione di tuples abbiamo anche std::tie(). Ciò consentirà di utilizzare una tupla di un gruppo di variabili e chiamare una funzione di confronto contro tutte. Puoi usarlo come

struct Foo 
{ 
    int a,b,c,d,e,f; 
    bool operator==(const Foo& rhs) { return std::tie(a,b,c,d,e,f) == std::tie(rhs.a,rhs.b,rhs.c,rhs.d,rhs.e,rhs.f); } 
}; 

Devi ancora elencare tutti i membri che vuoi controllare ma lo rende più facile. Puoi anche usarlo per semplificare molto meno e più dei confronti.

Si noti inoltre che le variabili vengono verificate nell'ordine in cui vengono fornite a tie. Questo è importante per meno e più dei confronti.

std::tie(a,b) < std::tie(rhs.a, rhs.b); 

non deve essere lo stesso di

std::tie(b,a) < std::tie(rhs.b, rhs.a); 
5

Al momento, non ci sono scorciatoie, ma ci sono piani per aggiungere il supporto per esso (P0221R0).

Bjarne Stroustrup ha recentemente scritto un post su di esso: A bit of background for the default comparison proposal

In C++ 14, non c'è niente di meglio che l'elenco di tutti i membri e il confronto loro, che è soggetto a errori.Per citare Bjarne:

L'argomento killer per i confronti di default non è in realtà la convenienza, ma il fatto che le persone ottengano i loro operatori di uguaglianza sbagliati.

1

Non è possibile una soluzione correlata a operator==. È possibile generare il codice correlato da una tabella di definizione con l'aiuto del cosiddetto numero X-Macro. La tabella potrebbe assomigliare

#define MEMBER_TBL     \ 
/*type  ,name ,default*/  \ 
X(int   ,_(i) ,42 )   \ 
X(float  ,_(f) ,3.14 )   \ 
X(std::string , t ,"Hello")   \ 

La roba _() è necessaria per evitare un finale , sulla generazione della chiamata std::tie(). Assicurati che l'ultimo elemento sia w.o. _(). L'utilizzo per generare i soci è:

struct Foo 
{ 
#define _(x) x 
#define X(type, name, default) type name{default}; 
    MEMBER_TBL 
#undef X 
#undef _ 
} 

Questo genera:

struct Foo 
{ 
    int i{42}; float f{3.14}; std::string t{"Hello"}; 
} 

Per generare il operator== è possibile utilizzare:

bool operator==(Foo const& other) const { 
     return std::tie(
#define _(x) x, 
#define X(type, name, default) this->name 
      MEMBER_TBL 
#undef X 
     ) == std::tie(
#define X(type, name, default) other.name 
      MEMBER_TBL 
#undef X 
#undef _ 
     ); 
    } 

che si traduce in

bool operator==(Foo const& other) const { 
    return std::tie(
        this->i, this->f, this->t 
    ) == std::tie(
        other.i, other.f, other.t 
    ); 
} 

Per aggiungere n nuovi membri puoi aggiungere semplicemente una nuova voce alla prima tabella. Tutto il resto viene generato automaticamente.

Un altro vantaggio è, è possibile aggiungere semplicemente un dump() metodo come

void print(void) const { 
    #define STR(x) #x 
    #define _(x) x 
    #define X(type, name, default)   \ 
      std::cout <<      \ 
       STR(name) << ": " << name << " "; 
      MEMBER_TBL 
    #undef X 
    #undef _ 
    #undef STR 
      std::cout << std::endl; 
     } 

che si traduce in

void print() const { 
    std::cout << "i" << ": " << i << " "; std::cout << "f" << ": " << f << " "; std::cout << "t" << ": " << t << " "; 
    std::cout << std::endl; 
} 

Tutte le informazioni relative ai membri potrebbe essere aggiunto alla tabella in un unico posto (singolo punto di informazione) ed estratto altrove necessario.

A lavoro Demo.

+0

Vale la pena ricordare che questa tecnica dovrebbe essere riservata solo a casi molto attesi. Ad esempio, l'ho usato in un esempio in cui avevo una classe di parametri di configurazione. Ogni volta che dovevi introdurre un nuovo parametro, dovevi aggiornare diverse posizioni nel codice, il che era abbastanza incline agli errori. L'IMHO che utilizza l'approccio Macro X ha semplificato la manutenzione, ma rimane un po 'controverso, quindi lo consiglierei solo come ultima risorsa. Ma sapere che la tecnica esiste, è davvero utile. –