2015-02-01 13 views
7

con C++ 14 siamo autorizzati a confrontare elementi di alcuni contenitori associativi (come std :: set) con altri tipi rispetto a quelli memorizzati in un contenitore. Dovrebbe funzionare quando il comparatore ha il valore is_transparent denotato come tipo (ad esempio, std::set::find).Come si usa il comparatore con is_transparent type?

Supponiamo di avere uno string wrapper che esegue alcuni controlli su una stringa (se il formato è formato valido e così via - non è molto importante, ma la costruzione è abbastanza pesante che vorrei evitarlo + può lanciare eccezioni) ed è memorizzato in std :: set per avere un contenitore di valori univoci. Come dovrei scrivere un comparatore per questo? Dovrebbe assomigliare a quello qui sotto? Posso sovraccaricare e usare il mio sw::operator<() per ottenere lo stesso risultato?

class sw 
{ 
public: 
    explicit sw(const std::string& s) : s_(s) { /* dragons be here */ } 
    const std::string& getString() const { return s_; } 

    bool operator<(const sw& other) const { return s_ < other.s_; } 

private: 
    std::string s_; 
}; 

struct Comparator 
{ 
    using is_transparent = std::true_type; 

    bool operator()(const sw& lhs, const std::string& rhs) const { return lhs.getString() < rhs; } 
    bool operator()(const std::string& lhs, const sw& rhs) const { return lhs < rhs.getString(); } 
    bool operator()(const sw& lhs, const sw& rhs) const { return lhs < rhs; } 
}; 

int main() 
{ 
    std::set<sw, Comparator> swSet{ sw{"A"}, sw{"B"}, sw{"C"} }; 
    std::cout << std::boolalpha << (swSet.find(std::string("A")) != swSet.end()) << std::endl; 
} 

ritengo che codice precedente dovrebbe funzionare come previsto, ma quando ho provato con g ++ 4,9 e clang ++ 3.6, sia ceduta errori di perdere conversione da string a key_type come se sovraccarichi stringa di Comparator::operator() erano mai preso in considerazione. Mi sto perdendo qualcosa?

+4

La versione di libstdC++ (la libreria standard di gcc) fornita con g ++ 4.9 non ha implementato la ricerca eterogenea. Il tuo codice [compila bene con clang e libC++] (http://coliru.stacked-crooked.com/a/97c0421099d7912d). –

+0

@ T.C .: grazie, ha senso ed è quello che temevo ... Ho provato con clang e -stdlib = libC++ e ha funzionato come previsto. Peccato che il mio ambiente predefinito sia g ++ e libstdC++. –

+4

È implementato in https://gcc.gnu.org/viewcvs/gcc?view=revision&revision=219888, meno di due settimane fa. –

risposta

3

Sì, che il codice è corretto, ma sarebbe più semplice sovraccaricare operator< per consentire il confronto con il tipo di std::string e poi basta usare std::less<> (cioè std::less<void>) che è "trasparente" già.

inline bool operator<(const sw& lhs, const std::string& rhs) { return lhs.getString() < rhs; } 
inline bool operator<(const std::string& lhs, const sw& rhs) { return lhs < rhs.getString(); } 

std::set<sw, std::less<>> swSet{ sw{"A"}, sw{"B"}, sw{"C"} }; 

Inoltre, è forse la pena notare che non importa quello che si definisce is_transparent a, uno di questi avrebbe lo stesso effetto di vostra definizione di esso:

using is_transparent = std::false_type; 

using is_transparent = void; 
+0

Huh, non sapevo "meno " e il suo comportamento, è bello. Tuttavia, quando faccio cose del genere, trovo un comparatore separato più pulito e più espressivo, ma probabilmente è una preferenza personale. :) –