2015-11-09 24 views
5

Ho visto che cosa sembra un'incoerenza nelle convenzioni std :: lower_bound() e std :: upper_bound() (beh, type-conversion, davvero) e mi chiedevo se qualcuno potesse chiarire per favore? Per i commenti, la riga 2 non verrà compilata nonostante la sua evidente somiglianza con la linea 1; è necessario utilizzare il modulo indicato sulla linea 3 (su gcc 4.7.3/ubuntu 64 bit, almeno - è tutto quello che ho avuto modo di giocare con)requisiti di valore incoerenti upper_bound e lower_bound

#include <set> 
#include <algorithm> 

using namespace std; 

class MyInt { 
    private: 
    int val; 
    public: 
    MyInt(int _val): val(_val) {} 
    bool operator<(const MyInt& other) const {return val < other.val;} 
}; 

int main() { 
    set<MyInt> s; 
    s.insert(1); // demonstrate implicit conversion works 
    s.insert(MyInt(2)); 
    s.insert(3); // one last one for the road 
    set<MyInt>::iterator itL = lower_bound(s.begin(), s.end(), 2); //LINE 1 
    // the line below will NOT compile 
    set<MyInt>::iterator itU = upper_bound(s.begin(), s.end(), 2); //LINE 2 
    // the line below WILL compile 
    set<MyInt>::iterator itU2 = upper_bound(s.begin(), s.end(), MyInt(2)); // LINE 3 
    return 0; 
} 
+0

Stesso comportamento con g ++ 4.8.4 qui. È sicuramente un bug g ++. –

risposta

5

Io non credo che sia un bug. Se si guarda alla (possible) implementation of std::upper_bound, il confronto viene fatto come

if (!(value < *it)) { ... } // upper_bound, implicit conversion `MyInt`->`int` doesn't work 

E poiché operator< è una funzione di membro di MyInt (e non di int, che non è un tipo di classe), il codice non viene compilato, dal momento che non c'è conversione da MyInt a int. D'altra parte, in std::lower_bound, *it appare sul lhs del confronto e value (di tipo int) può essere convertito implicitamente in MyInt passata a MyInt::operator<.

if (*it < value) { ... } // lower_bound, implicit conversion `int`->`MyInt` works 

Questo è il motivo per cui è meglio implementare operatori di confronto come i non soci, in modo da non avere questa asimmetria. Questo è anche menzionato nello di Scott Meyers Effective C++ book: Articolo 24: dichiara le funzioni non membro quando le conversioni di tipo devono essere applicate a tutti i parametri.

rapido e sporco fix: definire un MyInt::operator int(){return val;} per la conversione implicita MyInt a int. (EDIT: non funziona davvero, ambiguità). Ciò che funziona è la rimozione della necessità di conversione implicita

set<MyInt>::iterator itU = upper_bound(s.begin(), s.end(), MyInt(2)); 

invece.

+0

buon scavare lì, grazie! Interessante che l'implementazione STL (discutibilmente) confonda la sua interfaccia. Ancora un altro motivo per odiare le conversioni di tipo implicito – aho

+0

@aho Sì, una ragione super buona, ottima domanda da btw. Probabilmente "std :: upper_bound" può essere "corretto" se eseguiamo un'ulteriore conversione da 'value' a' * it' e lo memorizziamo in un temporaneo, quindi usiamo quest'ultimo nel confronto. In questo caso, il comportamento sarà esattamente simile a 'std :: lower_bound', e non vi è alcun prezzo da pagare (lo si paga comunque se si desidera compilare il codice). – vsoftco