Cần trình lặp khi sử dụng vòng lặp for dựa trên phạm vi


84

Hiện tại, tôi chỉ có thể thực hiện các vòng lặp dựa trên phạm vi với cái này:

for (auto& value : values)

Nhưng đôi khi tôi cần một trình lặp cho giá trị, thay vì một tham chiếu (Vì bất kỳ lý do gì). Có phương pháp nào mà không cần phải đi qua toàn bộ vector so sánh các giá trị không?

Câu trả lời:


77

Sử dụng forvòng lặp cũ như:

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

Với điều này, bạn valuecũng như trình lặp it. Sử dụng bất cứ điều gì bạn muốn sử dụng.


BIÊN TẬP:

Mặc dù tôi không đề xuất điều này, nhưng nếu bạn muốn sử dụng forvòng lặp dựa trên phạm vi (vâng, Vì lý do gì : D), thì bạn có thể làm điều này:

 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
 }

Cách tiếp cận này tránh tìm kiếm đã cho value, vì valueitluôn đồng bộ.


Vâng, đây là những gì tôi đang làm. Tôi chỉ tự hỏi liệu có giải pháp nào với các vòng lặp dựa trên phạm vi thay thế hay không
小 太郎

4
Tôi đồng ý rằng giải pháp đầu tiên với vòng lặp for cũ tốt hơn nhiều: P
小 太郎

@ 小 太郎: Hoặc bạn có thể sử dụng std::findnếu những gì bạn cần là xác định một giá trị ... Các thuật toán cũ tốt vẫn ở tiêu chuẩn mới.
David Rodríguez - dribeas

1
@David: Nếu có bản sao trong vectơ thì sao? valueitcó thể không đồng bộ. Hãy nhớ valuelà một tài liệu tham khảo.
Nawaz

9
@Nawaz: Tôi nghĩ rằng tôi đã hiểu sai câu cuối cùng. Tôi nghĩ rằng anh ấy đang sử dụng phạm vi dựa trên for để định vị một đối tượng đã biết. BTW, thích ++itđể it++bất cứ khi nào có thể (cả hai sử dụng trong mã của bạn) vì nó có thể có một chi phí thấp hơn.
David Rodríguez - dribeas

15

Đây là một lớp trình bao bọc proxy để cho phép bạn hiển thị trình vòng lặp ẩn bằng cách đặt bí danh cho biến của riêng bạn.

#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 ); }

Sử dụng:

#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

Tôi đã thử bản thân mình về điều này và tìm ra giải pháp.

Sử dụng:

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

Việc thực hiện không quá khó:

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);
}

à, vâng. Tôi không hiểu lắm, rằng trình biên dịch có thể lấy T của anh ấy từ hàm tạo ... vì vậy tôi nghĩ đến kiểu khai báo và thấy cách sử dụng-bloat ... và tôi không thấy rằng nó có thể lấy T của anh ấy từ một hàm ... chức năng mẫu, cảm ơn. Có đúng không, tôi phải làm thế nào bây giờ?
tải trọng

2
Vâng, điều đó có vẻ tốt. FWIW, có boost::counting_iteratordù, mà làm chính xác điều đó, và được gói thuận tiện với boost::counting_range, vì vậy bạn có thể viết: for(auto it : boost::counting_range(r.begin(), r.end())). :)
Xeo

1
Tôi nghĩ rằng operator++()nên trả lại một InnerIterator, nếu không thì rất tốt và đẹp.
Ben Voigt

2

forvòng lặp dựa trên phạm vi được tạo như bản đối chiếu c ++ foreachtrong java cho phép lặp lại các phần tử mảng dễ dàng. Nó có nghĩa là để loại bỏ việc sử dụng các cấu trúc phức tạp như trình vòng lặp để làm cho nó trở nên đơn giản. Tôi bạn muốn một iterator, như Nawaz đã nói, bạn sẽ phải sử dụng forvòng lặp bình thường .


Tôi muốn họ sẽ đưa ra một vòng lặp tương tự lặp được sử dụng thay vào đó, mặc dù :(
小太郎

1
Tôi rất vui vì những gì bạn nhận được là họ đánh giá cao chứ không phải trình lặp, đối với tôi, phạm vi dựa trên forđường cú pháp và về việc giảm lượng nhập. Phải dereference iterator sẽ làm cho nó dễ bị lỗi, đặc biệt là khi sử dụng vớiauto
TeaOverflow

2

Có một cách rất đơn giản để làm điều này std::vector, cũng sẽ hoạt động nếu bạn đang thay đổi kích thước vectơ trong quá trình này (tôi không chắc liệu câu trả lời được chấp nhận có xem xét trường hợp này hay không)

Nếu blà vectơ của bạn, bạn chỉ có thể làm

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

đâu itersẽ là trình lặp yêu cầu của bạn.

Điều này tận dụng lợi thế của thực tế là các vectơ C ++ luôn luôn kề nhau .


2
Nếu bạn đã khai thác thực tế là các vectơ C ++ liền kề nhau, bạn cũng có thể khai thác thực tế rằng bất kỳ triển khai lành mạnh nào sẽ chỉ gõ vector<T>::iteratorvào T*: Kiểm tra bằng a static_assert(), sau đó chỉ cần sử dụng T* iter = &i;.
cmaster - phục hồi monica

1

Hãy làm điều đó rất bẩn ... Tôi biết, 0x70h đang thay đổi với cách sử dụng ngăn xếp, phiên bản trình biên dịch, .... Nó nên được trình biên dịch hiển thị, nhưng nó không :-(

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

1
Tôi không có lời nào, điều này sai ở rất nhiều cấp độ, tôi thậm chí không biết bắt đầu phê bình nó từ đâu.
swineone
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.