2015-11-09 12 views
5

Widziałem coś, co wygląda na niespójność w std :: lower_bound() i std :: upper_bound() (dobrze, konwersja typu, naprawdę) i zastanawiałem się, czy ktoś mógłby to wyjaśnić? Zgodnie z komentarzem wiersz 2 nie będzie się kompilował pomimo oczywistego podobieństwa do wiersza 1; trzeba użyć postać pokazaną na linii 3 (na gcc 4.7.3/ubuntu 64-bitowych przynajmniej - to wszystko co mam do zabawy)Niespójne wymagania dotyczące wartości nadrzędnych i dolnych:

#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

To samo zachowanie z g ++ 4.8.4 tutaj. Z pewnością jest to błąd g ++. –

Odpowiedz

5

Nie sądzę, że to błąd. Jeśli spojrzeć na (possible) implementation of std::upper_bound porównanie odbywa się jak

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

A ponieważ operator< jest funkcją członkiem MyInt (i nie stanowi int, który nie jest typem klasy), kod nie skompilować, ponieważ nie ma konwersji z MyInt na int. Z drugiej strony, w std::lower_bound, *it pojawia się na lhs porównania, a value (typu int) można niejawnie przekonwertować na MyInt po przekazaniu do MyInt::operator<.

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

Jest to powód, dla którego lepiej jest wprowadzić operatorów porównania jako osoby niebędące członkami, więc nie masz tej asymetrii. Jest to również wymienione w Scott Meyers 'Effective C++ książka: Pozycja 24: Zadeklaruj funkcje nie będące członkami, gdy konwersje typu powinny mieć zastosowanie do wszystkich parametrów.

Szybki i brudny fix: zdefiniować MyInt::operator int(){return val;} dla niejawna konwersja MyInt do int. (EDYCJA: tak naprawdę nie działa, niejednoznaczność). To, co działa, eliminuje potrzebę niejawnej konwersji zamiast tego.

+0

dobre kopanie tam, dzięki! Interesujące jest to, że implementacja STL (prawdopodobnie) ogranicza jego interfejs. Kolejny powód do nienawiści do ukrytych konwersji typów – aho

+0

@aho Tak, bardzo dobry powód, świetne pytanie. 'Std :: upper_bound' może prawdopodobnie zostać" naprawiony ", jeśli dokonamy dodatkowej konwersji z' value' na '* it' i zapiszemy go w tymczasowym, a następnie użyjemy go w porównaniu. W tym przypadku zachowanie będzie dokładnie podobne do 'std :: lower_bound', i nie ma ceny do zapłacenia (płacisz tak czy nie, jeśli chcesz skompilować kod). – vsoftco

Powiązane problemy