ต้องการตัววนซ้ำเมื่อใช้ ranged-based สำหรับลูป


85

ขณะนี้ฉันสามารถทำลูปตามระยะห่างได้เท่านั้น:

for (auto& value : values)

แต่บางครั้งฉันต้องการตัววนซ้ำค่าแทนการอ้างอิง (ไม่ว่าด้วยเหตุผลใดก็ตาม) มีวิธีใดบ้างที่ไม่ต้องผ่านเวกเตอร์เปรียบเทียบค่าทั้งหมด?

คำตอบ:


78

ใช้forลูปเก่าเป็น:

for (auto it = values.begin(); it != values.end();  ++it )
{
       auto & value = *it;
       //...
}

ด้วยวิธีนี้คุณได้valueเช่นเดียวกับ ititerator ใช้อะไรก็ได้ที่คุณต้องการใช้


แก้ไข:

แม้ว่าฉันจะไม่แนะนำสิ่งนี้ แต่ถ้าคุณต้องการใช้forลูปตามช่วง(ใช่ไม่ว่าจะด้วยเหตุผลใดก็ตาม : D) คุณสามารถทำได้:

 auto it = std::begin(values); //std::begin is a free function in C++11
 for (auto& value : values)
 {
     //Use value or it - whatever you need!
     //...
     ++it; //at the end OR make sure you do this in each iteration
 }

วิธีการนี้จะหลีกเลี่ยงการค้นหาที่ได้รับvalueตั้งแต่valueและitอยู่เสมอในการซิงค์


ใช่นี่คือสิ่งที่ฉันทำ ฉันแค่สงสัยว่ามีวิธีแก้ปัญหาด้วยการวนซ้ำตามระยะแทนหรือไม่
小太郎

4
ฉันยอมรับว่าวิธีแก้ปัญหาแรกด้วยตัวเก่าสำหรับ loop นั้นดีกว่ามาก: P
小太郎

@ 小太郎: หรือคุณสามารถใช้std::findหากสิ่งที่คุณต้องการคือการค้นหาค่า ... อัลกอริทึมเก่าที่ดียังอยู่ในมาตรฐานใหม่
David Rodríguez - dribeas

1
@ เดวิด: จะเกิดอะไรขึ้นถ้ามีข้อมูลซ้ำกันในเวกเตอร์? valueและitอาจไม่ซิงค์กัน จำไว้valueเป็นข้อมูลอ้างอิง
Nawaz

9
@Nawaz: ฉันคิดว่าฉันเข้าใจประโยคสุดท้ายผิด ฉันคิดว่าเขากำลังใช้ช่วงตามเพื่อค้นหาวัตถุที่รู้จัก BTW ชอบ++itไปit++เมื่อใดก็ตามที่เป็นไปได้ (ทั้งการใช้งานในรหัสของคุณ) ในขณะที่มันอาจจะมีค่าใช้จ่ายน้อยกว่า
David Rodríguez - dribeas

15

นี่คือคลาส Wrapper ของพร็อกซีเพื่อให้คุณสามารถแสดงตัววนซ้ำที่ซ่อนอยู่ได้โดยการกำหนดนามแฝงให้กับตัวแปรของคุณเอง

#include <memory>
#include <iterator>

/*  Only provides the bare minimum to support range-based for loops.
    Since the internal iterator of a range-based for is inaccessible,
    there is no point in more functionality here. */
template< typename iter >
struct range_iterator_reference_wrapper
    : std::reference_wrapper< iter > {
    iter &operator++() { return ++ this->get(); }
    decltype( * std::declval< iter >() ) operator*() { return * this->get(); }
    range_iterator_reference_wrapper( iter &in )
        : std::reference_wrapper< iter >( in ) {}
    friend bool operator!= ( range_iterator_reference_wrapper const &l,
                             range_iterator_reference_wrapper const &r )
        { return l.get() != r.get(); }
};

namespace unpolluted {
    /*  Cannot call unqualified free functions begin() and end() from 
        within a class with members begin() and end() without this hack. */
    template< typename u >
    auto b( u &c ) -> decltype( begin( c ) ) { return begin( c ); }
    template< typename u >
    auto e( u &c ) -> decltype( end( c ) ) { return end( c ); }
}

template< typename iter >
struct range_proxy {
    range_proxy( iter &in_first, iter in_last )
        : first( in_first ), last( in_last ) {}

    template< typename T >
    range_proxy( iter &out_first, T &in_container )
        : first( out_first ),
        last( unpolluted::e( in_container ) ) {
        out_first = unpolluted::b( in_container );
    }

    range_iterator_reference_wrapper< iter > begin() const
        { return first; }
    range_iterator_reference_wrapper< iter > end()
        { return last; }

    iter &first;
    iter last;
};

template< typename iter >
range_proxy< iter > visible_range( iter &in_first, iter in_last )
    { return range_proxy< iter >( in_first, in_last ); }

template< typename iter, typename container >
range_proxy< iter > visible_range( iter &first, container &in_container )
    { return range_proxy< iter >( first, in_container ); }

การใช้งาน:

#include <vector>
#include <iostream>
std::vector< int > values{ 1, 3, 9 };

int main() {
    // Either provide one iterator to see it through the whole container...
    std::vector< int >::iterator i;
    for ( auto &value : visible_range( i, values ) )
        std::cout << "# " << i - values.begin() << " = " << ++ value << '\n';

    // ... or two iterators to see the first incremented up to the second.
    auto j = values.begin(), end = values.end();
    for ( auto &value : visible_range( j, end ) )
        std::cout << "# " << j - values.begin() << " = " << ++ value << '\n';
}

13

ฉันลองทำด้วยตัวเองและพบวิธีแก้ปัญหา

การใช้งาน:

for(auto i : ForIterator(some_list)) {
    // i is the iterator, which was returned by some_list.begin()
    // might be useful for whatever reason
}

การนำไปใช้นั้นไม่ยาก:

template <typename T> struct Iterator {
    T& list;
    typedef decltype(list.begin()) I;

    struct InnerIterator {
        I i;
        InnerIterator(I i) : i(i) {}
        I operator * () { return i; }
        I operator ++ () { return ++i; }
        bool operator != (const InnerIterator& o) { return i != o.i; }
    };

    Iterator(T& list) : list(list) {}
    InnerIterator begin() { return InnerIterator(list.begin()); }
    InnerIterator end() { return InnerIterator(list.end()); }
};
template <typename T> Iterator<T> ForIterator(T& list) {
    return Iterator<T>(list);
}

ใช่ใช่ ฉันไม่ค่อยเข้าใจเลยว่าคอมไพเลอร์สามารถรับ T ของเขาจากตัวสร้าง ... ดังนั้นฉันจึงนึกถึงการปฏิเสธประเภทและเห็นการขยายตัวของการใช้งาน ... และฉันไม่เห็นว่ามันสามารถรับ T ของเขาจากฟังก์ชันได้ ... แม่แบบฟังก์ชันขอบคุณ ใช่แล้วฉันจะทำอย่างไรตอนนี้?
payload

2
ใช่ว่าดูดี FWIW boost::counting_iteratorแม้ว่าจะมีสิ่งที่ทำอย่างนั้นและถูกห่อด้วยอย่างสะดวกboost::counting_rangeดังนั้นคุณสามารถเขียน: for(auto it : boost::counting_range(r.begin(), r.end())). :)
Xeo

1
ฉันคิดว่าoperator++()ควรกลับมาเป็นInnerIteratorอย่างอื่นที่ดีและน่าเกลียดมาก
Ben Voigt

2

forลูปตามช่วงถูกสร้างขึ้นเป็นคู่ c ++ สำหรับforeachใน java ซึ่งช่วยให้สามารถทำซ้ำองค์ประกอบอาร์เรย์ได้ง่าย มีไว้สำหรับการลบการใช้โครงสร้างที่ซับซ้อนเช่นตัวทำซ้ำเพื่อให้ง่าย ฉันต้องการiteratorอย่างที่นาวาซบอกคุณจะต้องใช้forลูปปกติ


ฉันหวังว่าพวกเขาจะเสนอลูปคล้าย ๆ กันที่ใช้ตัววนซ้ำแทนแม้ว่า :(
小太郎

1
ฉันมีความสุขที่สิ่งที่คุณได้รับคือสิ่งที่พวกเขาให้ความสำคัญไม่ใช่ตัวทำซ้ำสำหรับฉันตามช่วงforคือน้ำตาลไวยากรณ์และการลดปริมาณการพิมพ์ การต้องหักล้างตัวทำซ้ำจะทำให้เกิดข้อผิดพลาดได้ง่ายโดยเฉพาะเมื่อใช้กับauto
TeaOverflow

2

มีวิธีง่ายๆในการทำสิ่งนี้std::vectorซึ่งควรได้ผลเช่นกันหากคุณกำลังปรับขนาดเวกเตอร์ในระหว่างกระบวนการ (ฉันไม่แน่ใจว่าคำตอบที่ยอมรับจะพิจารณากรณีนี้หรือไม่)

ถ้าbเป็นเวกเตอร์ของคุณคุณก็ทำได้

for(auto &i:b){
    auto iter = b.begin() + (&i-&*(b.begin()));
}

ที่iterจะเป็นตัวทำซ้ำที่คุณต้องการ

นี้ใช้ประโยชน์จากความจริงที่ว่าC ++ เวกเตอร์ที่ต่อเนื่องกันอยู่เสมอ


2
หากคุณมีอยู่แล้วการใช้ประโยชน์จากความจริงที่ว่า C ++ เวกเตอร์มีความต่อเนื่องกันคุณอาจรวมทั้งยังใช้ประโยชน์จากความจริงที่ว่าการดำเนินการใด ๆ ที่จะมีสติเพียง typedef vector<T>::iteratorไปT*: ตรวจสอบว่ามีแล้วก็ใช้static_assert() T* iter = &i;
cmaster - คืนสถานะโมนิกา

1

ลองทำดูสกปรกมาก ... ฉันรู้ว่า 0x70h กำลังเปลี่ยนไปตามการใช้งานสแต็กเวอร์ชันคอมไพเลอร์ .... คอมไพเลอร์ควรเปิดเผย แต่มันไม่ใช่ :-(

char* uRBP = 0; __asm { mov uRBP, rbp }
Iterator** __pBegin = (Iterator**)(uRBP+0x70);
for (auto& oEntry : *this) {
    if (oEntry == *pVal) return (*__pBegin)->iPos;
}

1
ฉันไม่มีคำพูดนี้ผิดในหลายระดับฉันไม่รู้ด้วยซ้ำว่าจะเริ่มวิจารณ์มันที่ไหน
สุกร
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.