2016-07-15 4 views
0

Gestern hatte ich das Glück, eine Frage zu sehen, die für eine kurze Zeit existierte und gelöscht wurde, während ich eine Antwort darauf schrieb (ich denke, dass die Frage vom Autor gelöscht wurde, der von Kommentatoren überzeugt war, dass sein Ihr Vorschlag machte keinen Sinn. Ich formuliere diese Frage etwas allgemeiner und gebe meine Antwort darauf. Andere Ideen und/oder Hinweise auf bestehende Implementierungen meiner Idee (die ich nicht schnell finden konnte) sind ebenfalls willkommen.Wie vereinfacht man die Überprüfung, ob ein von einem Suchalgorithmus zurückgegebener Iterator gültig ist?

Das Folgende ist ein ziemlich häufiges Muster einer Suchoperation der Verwendung (siehe unten Beispiel wird die Elementfunktion find() von std::map, aber es kann auch über den std::find oder std::lower_bound Algorithmen neu geschrieben werden):

const auto it = map.find(key);  // 1 
if (it != map.end())    // 2 
{ 
    do something with it 
} 

obwohl das Muster ziemlich kurz sieht, kann der Code mehr überladen werden, wenn das Kartenobjekt einen wesentlich längeren Namen hat oder wie unten durch einen komplexen Ausdruck verwiesen:

const auto it = p->getMappingFor(currentConfig()->whatever()).find(key); 

Gibt es eine Möglichkeit, die Zeilen 1 und 2 im obigen Beispiel in eins zusammenzufassen und das Kartenobjekt nur einmal referenzieren zu lassen?

EDIT 1:

Die Motivation zur Vereinfachung dieses Suchen und überprüfen Muster ist ähnlich dem, der als eine bequemere Form des traditional for loop auf die Erscheinung des range-based for loop geführt.

+0

Verwenden Sie Bereiche anstelle von Iteratoren. – lorro

Antwort

0

Die kürzeste Form etwas in einem Behälter suchen und sofort die Gültigkeit der zurückgegebene Iterator Überprüfung ist dies:

if (const auto it = find_in(container, something)) 
{ 
    do something with it 
} 

dies für die Art der it zu kompilieren, muss bool konvertierbar sein. (Es versteht sich von selbst, dass it vom regulären Iteratortyp abgeleitet sein muss, der von der zugrunde liegenden Suchoperation zurückgegeben würde). Der folgende Code enthält die Implementierung des find_in()-Dienstprogramms für std::vector und std::map. Das Hinzufügen von Überladungen für andere Container ist trivial.

#include <algorithm> 
#include <vector> 
#include <map> 
#include <iostream> 

template<class It> 
class OptionalIterator : public It 
{ 
    It end_; 

public: 
    OptionalIterator(It it, It end) : It(it), end_(end) {} 

    explicit operator bool() const 
    { 
     return static_cast<const It&>(*this) != end_; 
    } 
}; 

////////////////////// find_in() a range /////////////////////////////////// 

template<class It, class X> 
OptionalIterator<It> 
find_in(It start, It end, const X& x) 
{ 
    return OptionalIterator<It>(std::find(start, end, x), end); 
} 

////////////////////// find_in() a std::vector ///////////////////////////// 

template<class T, class A, class X> 
OptionalIterator<typename std::vector<T, A>::const_iterator> 
find_in(const std::vector<T, A>& v, const X& x) 
{ 
    return find_in(v.begin(), v.end(), x); 
} 

template<class T, class A, class X> 
OptionalIterator<typename std::vector<T, A>::iterator> 
find_in(std::vector<T, A>& v, const X& x) 
{ 
    return find_in(v.begin(), v.end(), x); 
} 

////////////////////// find_in() a std::map //////////////////////////////// 

template<class K, class V, class C, class A, class X> 
OptionalIterator<typename std::map<K, V, C, A>::const_iterator> 
find_in(const std::map<K, V, C, A>& m, const X& x) 
{ 
    typedef typename std::map<K, V, C, A>::const_iterator It; 
    return OptionalIterator<It>(m.find(x), m.end()); 
} 

template<class K, class V, class C, class A, class X> 
OptionalIterator<typename std::map<K, V, C, A>::iterator> 
find_in(std::map<K, V, C, A>& m, const X& x) 
{ 
    typedef typename std::map<K, V, C, A>::iterator It; 
    return OptionalIterator<It>(m.find(x), m.end()); 
} 

//////////////////////////////////////////////////////////////////////////// 

int main() 
{ 
    std::vector<int> v{2, 4, 1, 3}; 
    if (const auto it = find_in(v, 4)) 
      std::cout << "4 in v is at index " << it - v.begin() << std::endl; 

    std::map<int, int> m{{1, 23}, {4, 56}}; 
    if (const auto it = find_in(m, 4)) 
      std::cout << "m[4]=" << it->second << std::endl; 

    return 0; 
} 
+0

Ein Iterator, "für sich allein", ist nicht in bool umwandelbar. Diese Konvertierung sollte in einem Fall wahr und in einem anderen Fall false zurückgegeben werden, wenn Sie Aufrufe zulassen. – lorro

+1

Dies funktioniert nicht mit Zeigern so ist es leider nicht generisch genug – Slava

+0

@Slava Aber es kann für Zeiger verallgemeinert werden, nicht wahr? – Leon

1

Gibt es eine Möglichkeit, die Linien 1 und 2 in dem folgenden Beispiel in einem einsturz und das Kartenobjekt haben nur einmal referenziert?

Ich verstehe die Motivation dafür nicht klar. Die Kombination mehrerer Abfragen in einem einzigen Liner führt zu einem Code, der schwieriger zu verstehen und zu debuggen ist. Ich werde vorschlagen, den 2-Liner zu einem 3-Liner anstatt zu einem Liner zu machen.

auto const& theMap = p->getMappingFor(currentConfig()->whatever()); 
auto const it = theMap.find(key); 
if (it != theMap.end()) 
{ 
    ... 
} 
0

Mit C++ 17 haben wir If statements with initializer.

Wir haben dann

if (const auto it = theMap.find(key); it != theMap.end()) { 
    ... 
} 

Ein Vorteil dieses schreiben kann, ist, dass der Iterator innerhalb des Umfangs der if lebt nur, einschließlich der möglichen else Klausel.

+1

Dies ist nicht wirklich eine Antwort auf die Frage von OP, da er möchte, dass die Map nur einmal referenziert wird – Slava

Verwandte Themen