2010-04-16 5 views
6

Ich bin verwirrt über die Schnittstelle von std::find. Warum braucht es kein Compare Objekt, das ihm sagt, wie man zwei Objekte vergleicht?Wie std :: mit einem Compare-Objekt finden?

Wenn ich könnte ein Compare Objekt übergeben ich den folgenden Code Arbeit machen könnte, wo ich von Wert vergleichen möchte, anstatt nur direkt die Zeigerwerte zu vergleichen:

typedef std::vector<std::string*> Vec; 
Vec vec; 
std::string* s1 = new std::string("foo"); 
std::string* s2 = new std::string("foo"); 
vec.push_back(s1); 
Vec::const_iterator found = std::find(vec.begin(), vec.end(), s2); 
// not found, obviously, because I can't tell it to compare by value 
delete s1; 
delete s2; 

Ist die folgende der empfohlene Weg es zu tun?

template<class T> 
struct MyEqualsByVal { 
    const T& x_; 
    MyEqualsByVal(const T& x) : x_(x) {} 
    bool operator()(const T& y) const { 
    return *x_ == *y; 
    } 
}; 
// ... 
vec.push_back(s1); 
Vec::const_iterator found = 
    std::find_if(vec.begin(), vec.end(), 
       MyEqualsByVal<std::string*>(s2)); // OK, will find "foo" 

Antwort

6

find kann nicht überladen werden, um ein unäres Prädikat anstelle eines Werts zu verwenden, da es sich um einen unbeschränkten Schablonenparameter handelt. Wenn Sie also find(first, last, my_predicate) aufrufen, gibt es eine mögliche Mehrdeutigkeit, ob das Prädikat für jedes Mitglied des Bereichs ausgewertet werden soll oder ob Sie ein Mitglied des Bereichs suchen möchten, der dem Prädikat selbst entspricht (es könnte sich um einen Bereich handeln von Prädikaten, die für alle Entwickler der Standardbibliotheken bekannt oder wichtig sind, oder der value_type des Iterators könnte sowohl in den Prädikattyp als auch in seinen argument_type konvertierbar sein. Daher die Notwendigkeit, find_if unter einem separaten Namen zu gehen.

find könnte überladen worden sein, um zusätzlich zu dem gesuchten Wert ein optionales binäres Prädikat zu übernehmen. Aber das Erfassen von Werten in Funktoren ist, wie Sie es getan haben, eine solche Standardtechnik, von der ich nicht überzeugt bin, dass sie ein enormer Gewinn wäre. Sie ist sicherlich niemals notwendig, da Sie mit find_if immer das gleiche Ergebnis erzielen können.

Wenn Sie die gewünschte find haben, müssten Sie noch einen Funktor schreiben (oder Boost verwenden), da <functional> nichts enthält, um einen Zeiger zu dereferenzieren. Ihr Funktor wäre allerdings etwas einfacher als binäres Prädikat, oder Sie könnten einen Funktionszeiger verwenden, also wäre es ein bescheidener Gewinn. Also ich weiß nicht, warum das nicht zur Verfügung gestellt wird. Angesichts der copy_if Fiasko bin ich nicht sicher, es gibt viel Wert in der Annahme, es gibt immer gute Gründe für Algorithmen, die nicht verfügbar sind :-)

+0

Danke! Aus Neugier, was ist falsch mit "copy_if"? – Frank

+1

@dehmann: Das einzige, was daran falsch ist, ist, dass es nicht im Standard ist. Es wurde im Wesentlichen aufgrund eines Schnittunfalls ausgelassen. –

2

Da Ihre T ist ein Zeiger, auch Sie können in dem Funktionsobjekt eine Kopie des Zeigers speichern.

Ansonsten ist es so und es gibt nicht viel mehr.

Abgesehen davon ist es keine gute Idee, blanke Zeiger in einem Container zu speichern, es sei denn, Sie sind sehr vorsichtig mit der Ausnahmesicherheit, die fast immer mühseliger ist, als sie wert ist.

+0

Und ... Ich habe die erste Hälfte der Frage völlig verpasst ... oops. Allerdings ist Steve Jessops Antwort besser, als ich es hätte erklären können. –

0

Genau das ist find_if - es braucht ein Prädikat, das zum Vergleichen von Elementen aufgerufen wird.