2015-05-08 21 views
6

Ero curioso di sapere il motivo per cui questo pezzo di codice non funziona:namespace std sovraccarico meno di

#include "stdafx.h" 
#include <iostream> 
#include <tuple> 
#include <string> 
#include <vector> 
#include <algorithm> 

typedef std::tuple<int, std::string> intString; 
bool operator<(intString& lhs, intString& rhs){ 
    return std::tie(std::get<1>(lhs), std::get<0>(lhs)) < std::tie(std::get<1>(rhs), std::get<0>(rhs)); 
} 

void printIntStrings(std::vector<intString>& v){ 
    for (intString& i : v){ 
     std::cout << std::get<0>(i) << " is " << std::get<1>(i) << std::endl; 
    } 
} 

int main(int argc, char* argv[]) 
{ 
    std::vector<intString> v; 
    v.push_back(std::make_tuple(5, "five")); 
    v.push_back(std::make_tuple(2, "two")); 
    v.push_back(std::make_tuple(9, "nine")); 
    printIntStrings(v); 
    std::sort(v.begin(), v.end()); 
    printIntStrings(v); 
    return 0; 
} 

Per quanto posso capire, ho semplicemente creare un vettore di intStrings e il mio operatore deve ordinare dal secondo elemento nella tupla primo quindi l'uscita dovrebbe essere (ultimi 3 linee comunque)

5 five 
9 nine 
2 two 

Tuttavia esecuzione sulla mia macchina ottengo

2 two 
5 five 
9 nine 

che implica che l'ordinamento utilizza l'operatore di default inferiore a, ignorando quello specificato. Nota, aggiungendo const prima che i parametri non sembrino influenzare nulla.

Ho trovato tre modi per "risolvere" questo.

Fix # 1

Surround bool operator < ... nel namespace std in questo modo:

namespace std{ 
    bool operator<(intString& lhs, intString& rhs){ 
     return std::tie(std::get<1>(lhs), std::get<0>(lhs)) < std::tie(std::get<1>(rhs), std::get<0>(rhs)); 
    } 
} 

Tuttavia mi è stato detto che dovremmo mai aggiungere le cose al namespace std dal momento che il comportamento è indefinito, quindi questa correzione sembra la peggiore.

Fix # 2

Aggiungere in qualcosa di personalizzato alla tupla in questo modo:

enum class TRASH{DOESNTMATTER}; 
typedef std::tuple<int, std::string, TRASH> intString; 
bool operator<(intString& lhs, intString& rhs){ 
    return std::tie(std::get<1>(lhs), std::get<0>(lhs)) < std::tie(std::get<1>(rhs), std::get<0>(rhs)); 
} 

(e, ovviamente, aggiungere TRASH :: doesntmatter come terzo argomento make_tuple) Tuttavia, questo sembrava come un sacco di lavoro per qualcosa di così semplice. Inoltre, sembra uno spreco poiché l'enum non è usato in modo significativo.

Fix # 3

utilizzare il predicato specie in questo modo:

std::sort(v.begin(), v.end(), operator<); 

Questo sembrava essere la soluzione più elegante. Tuttavia, non vedo perché devo dire esplicitamente al compilatore di usare il mio operatore definito <.

Quindi vorrei sapere:

1) perché questo accade? Non dovrei C++ trovare la mia implementazione e usarla?

2) quale "fix" è il migliore? se nessuno di quelli che ho trovato, cosa consiglieresti?

Qualche idea? Grazie per aver letto!

+1

Lo standard 'operator <' è una corrispondenza migliore perché i suoi parametri sono 'const'. – Oktalist

+0

@Oktalist Oops, ho dimenticato di menzionare ma aggiungere cost prima che entrambi i parametri non siano d'aiuto. edit edit –

+0

Il problema è che c'è _already_ un 'operator <' per le tuple, quindi il compilatore usa quello ufficiale piuttosto che il tuo hack. –

risposta

6

tuo operator< sovraccarico non è visibile in t indica dove viene utilizzato < (che si trova nel corpo di std::sort e/o qualsiasi funzione di supporto chiamata da esso, da qualche parte in <algorithm>).

Se deve essere utilizzato, deve essere rilevato dalla ricerca dipendente dall'argomento; ma non c'è nulla in std::tuple<int, std::string> che abbia lo spazio dei nomi globale come spazio dei nomi associato, quindi ADL non ti aiuta, e quello standard è usato.

Passarlo come un comparatore, preferibilmente utilizzando un oggetto lambda o funzione (che è meglio dei puntatori di funzione), è la soluzione più semplice. Raccomando anche di rinominarlo; avere un sovraccarico di operator< con semantica completamente diversa da quella standard, che può essere o non essere utilizzato dall'espressione a < b a seconda di dove si trova quell'espressione, non è una buona idea.

+0

Ok, ho pensato che fosse qualcosa del genere. Ci sarebbe un modo per "forzare" ADL a prenderlo? forse qualcosa sulla falsariga di usare namespace global (provato namespace globale e non ha funzionato) –

+1

@BillyWon Non senza cambiare il tipo. Inoltre non consiglio di scrivere un '<' che fa qualcosa di diverso da quello fornito dalla libreria standard. –

+0

Grazie! Cambierà in un nome più descrittivo. Lo avevo sovraccarico

-1

già risolvere il problema dal vostro auto

il problema è la vostra funzione di operatore di < non ignorare il tuple di default :: operator <, sono in diversi namespace

modo, sia il Fix # 1 e fissare # 3 sono buona soluzione

Fix # 1 metterli nella stessa namespace rendono ignorare corretto, credo che sia il modo migliore

+0

E causa un comportamento non definito nel processo. Sì, è sicuramente il modo migliore. –

Problemi correlati