2015-11-09 11 views
5

Ich sah, was aussieht wie eine Inkonsistenz in der std :: lower_bound() und std :: upper_bound() -Syntaxen (Nun, Typ-Konvertierung, wirklich) und fragte mich, ob jemand bitte erläutern könnte? Laut den Kommentaren wird Zeile 2 trotz ihrer offensichtlichen Ähnlichkeit zu Zeile 1 nicht kompiliert; Sie müssen das Formular auf der Linie 3 gezeigt verwenden (auf gcc 4.7.3/ubuntu 64-Bit zumindest - das ist alles, was ich habe, mit spielen)Obere_Bound und Lower_Bound Inkonsistente Wert Anforderungen

#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

Gleiches Verhalten mit g ++ 4.8.4 hier. Es ist definitiv ein g ++ Bug. –

Antwort

5

Ich glaube nicht, dass es ein Fehler ist. Wenn man sich die (possible) implementation of std::upper_bound aussieht, wird der Vergleich gemacht wie

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

Und weil operator< ist Mitglied Funktion von MyInt (und nicht den von int, die kein Klassentyp ist), wird der Code nicht kompiliert werden, da Es gibt keine Konvertierung von MyInt zu int. Auf der anderen Seite, in std::lower_bound, *it erscheint auf der Links des Vergleichs, und value (des Typs int) kann implizit in MyInt umgewandelt werden, wenn an MyInt::operator< übergeben.

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

Dies ist der Grund, warum es besser ist, zu Vergleichsoperator als Nicht-Mitglieder zu implementieren, so dass Sie nicht über diese Asymmetrie. Dies wird auch in Scott Meyers 'Effective C++ Buch: Punkt 24: Deklarieren Nichtmitgliedsfunktionen, wenn Typumwandlungen für alle Parameter gelten sollte.

quick and dirty fix: ein MyInt::operator int(){return val;} für implizite Konvertierung MyInt zu int definieren. (EDIT: funktioniert nicht wirklich, Ambiguität). Was funktioniert, ist das Entfernen der Notwendigkeit für die implizite Umwandlung

anstatt

.

+0

gut graben dort, danke! Interessant, dass die STL-Implementierung (wohl) ihre Schnittstelle verwechselt. Noch ein weiterer Grund, implizite Typumwandlungen zu verabscheuen – aho

+0

@aho Ja, ein super guter Grund, große Frage übrigens. Das 'std :: upper_bound' kann wahrscheinlich" repariert "werden, wenn wir eine zusätzliche Umwandlung von' value' nach '* it' vornehmen und diese in ein temporäres speichern und dann das letztere im Vergleich verwenden. In diesem Fall verhält sich das Verhalten genau wie "std :: lower_bound", und es gibt keinen zu zahlenden Preis (Sie zahlen es trotzdem, wenn der Code kompiliert werden soll). – vsoftco