2015-08-11 7 views
5

Ich habe eine std::vector<std::string> v; (initialisiert). Wie kann ich die Range-for-Schleife für den Zugriff auf alle Elemente mit Ausnahme der ersten (auf Index Null) verwenden. Für alle Elemente:C++ 11: Range-Loop-Vektor aus dem zweiten Element?

for (const string & s: v) 
    process(s); 

Anstelle des v ein Bereichsausdruck verwendet werden kann. Wie kann ich den Bereichsausdruck schreiben, um das erste Element zu überspringen (oder überspringe die ersten n Elemente) ?

Ich weiß, wie man den Effekt mit v.begin() + 1 und die Verwendung der klassischen Schleife. Ich suche nach der neuen, lesbareren, empfohlenen Alternative, um das zu tun. Möglicherweise etwas ähnlich Python Slicing? ... wie:

for s in v[1:]: 
    process(s) 
+0

Eine Möglichkeit ist es, einen neuen Vektor mit den notwendigen Elementen zu machen, und dann durch die Schleife. Oder Sie können 'std :: for_each' verwenden: http://en.cppreference.com/w/cpp/algorithm/for_each – CinCout

+3

Sie können nicht. Die bereichsbasierte Schleife ist nicht die Lösung für jedes Problem. Verwenden Sie einfach eine normale Schleife. –

+0

Wie von @GargAnkit erwähnt, können Sie einen neuen Vektor mit erforderlichen Elementen erstellen. Sie können dies als Teil der Schleifendeklaration mithilfe des Vektorkonstruktors tun. Also 'für (auto &&i: std :: vector (v.begin() + 1, v.end())). Demo [hier] (http://ideone.com/G9yfjx). Nicht so sauber wie das Python-Beispiel, das du gezeigt hast. – Ralara

Antwort

5

einen Wrapper erstellen, für die beginnen() und end() geben die richtigen Iteratoren zurück, und dann können Sie das als zweites Argument verwenden.

#include <iostream> 
#include <vector> 

template< typename Collection > 
class FromNth 
{ 
    Collection& coll_; 
    size_t offset_; 

public: 
    FromNth(Collection& coll, size_t offset) 
     : coll_(coll), offset_(offset) 
    { 
    } 

    // will nicely resolve to const_iterator if necessary 
    auto begin() const -> decltype(coll_.begin()) 
     { return coll_.begin() + offset_; } 

    auto end() const -> decltype(coll_.end()) 
     { return coll_.end(); } 
}; 

template< typename Collection > 
FromNth<Collection> makeFromNth(Collection& collection, size_t offset) 
{ 
    return FromNth<Collection>(collection, offset); 
} 

template< typename Collection > 
auto begin(const FromNth<Collection> & wrapper) -> decltype(wrapper.begin()) 
{ 
    return wrapper.begin(); 
} 

template< typename Collection > 
auto end(const FromNth<Collection> & wrapper) -> decltype(wrapper.end()) 
{ 
    return wrapper.end(); 
} 

int main() 
{ 
    std::vector<int> coll { 2, 3, 5, 7, 11, 13, 17, 19, 23 }; 

    for(auto x : makeFromNth(coll, 1)) 
    { 
     std::cout << x << '\n'; 
    } 
    return 0; 
} 

Beachten Sie, dass mein fromNth „begin“ ist nicht definiertes Verhalten, wenn die Größe des Eingangs kleiner als der Offset ist. (Wenn es gleich ist, ist es gut definiert und beginnt == Ende). Führen Sie daher zuerst eine Größenprüfung durch.

Hinweis: Wenn Sie eine ausreichend aktuelle Boost-Version verwenden, kann Ihnen iterator_range bereits eine solche "Sammlung" bereitstellen, die meinem "FromNth" ähnlich ist.

for(auto const& s : boost::make_iterator_range(v.begin() + 1, v.end())) 
{ 
    process(s); 
} 

Hinweis: Der Code oben gearbeitet CodingGround C++ 11 GNU 4.8.3 verwenden. (Diese Seite ist allerdings sehr langsam). Ab C++ 14 werden Sie die Anweisungen -> declltype (die in C++ 11 für Vorlagen benötigt werden) nicht benötigen.

Output:

sh-4.3$ g++ -std=c++11 -o main *.cpp 
sh-4.3$ main 
3 
5 
7 
11 
13 
17 
19 
23 
+0

Danke für die Information! Ich hatte schon erwartet, dass es einen solchen Code gibt (und ich konnte ihn nicht finden);) – pepr

5

Bis reicht es in der Standard-Bibliothek machen, werden Sie nicht besser als ein Vanille-for-Schleife einfach in C++:

for(auto i = begin(v) + 1, e = end(v); i !=e; ++i) 
    // Do something with *i 
+0

nicht wahr? Siehe meine Antwort. – CashCow

+1

@CashCow wie die Frage über das Biegen eines Bereichs-für in Form ist, nehme ich reines C++ an. Ich habe genug Boost gesehen, um zu wissen, dass * nichts * unmöglich ist, wenn man genug Vorlagen darauf wirft;) – Quentin

+0

boost's iterator_range löst es gut. Mine ist ein bisschen wie ein Iterator-Bereich, aber von einem nummerierten Offset wie der Benutzer angefordert. Natürlich kann ich iterator_range auch einfach neu schreiben. Der Benutzer fragt, ob es in C++ 11 getan werden kann und ich zeige, wie (nicht nur Boost sagen) – CashCow

Verwandte Themen