2013-05-24 32 views
12

Một STL xây dựng thông thường là:như thế nào std :: copy làm việc với lặp dòng

vector<string> col; 
copy(istream_iterator<string>(cin), istream_iterator<string>(), 
    back_inserter(col)); 

nơi mà chúng tôi sử dụng một istream_iterator để sao chép từ đầu vào std (cin) vào một vector.

Có ai có thể giải thích cách mã này hoạt động không?

vấn đề của tôi là tôi không thực sự hiểu phần này:

istream_iterator<string>(cin), istream_iterator<string>() 
+1

bạn nên đọc cuốn sách "The C++ STL" –

Trả lời

21

Trước tiên, lưu ý rằng trong trường hợp này, bạn không cần sử dụng bất cứ nhu cầu nào thực sự cần sử dụng std::copy. Bạn chỉ có thể khởi tạo vectơ trực tiếp từ các trình vòng lặp:

vector<string> col((istream_iterator<string>(cin)), 
        istream_iterator<string>()); 

Điều này có thể không làm cho mã dễ hiểu hơn nhiều.

Theo như cách mã hoạt động, nó có thể hơi căng thẳng hơn bạn tưởng.Một istream_iterator trông mơ hồ như thế này:

template <class T> 
class istream_iterator { 
    std::istream *is; 
    T data; 
public: 
    istream_iterator(std::istream &is) : is(&is) { ++(*this); } 
    istream_iterator() : is(nullptr) {} 

    T operator++() { (*is) >> data; return *this; } 
    T operator++(int) { (*is) >> data; return *this; } 

    T const &operator*() { return data; } 

    bool operator !=(istream_iterator &end) { return (*is).good(); } 
    bool operator ==(istream_iterator &end) { return !(*is).good(); } 
}; 

Rõ ràng là có nhiều hơn tôi bỏ qua, nhưng đó là hầu hết những gì chúng tôi quan tâm ở đây. Vì vậy, điều xảy ra là khi bạn tạo trình lặp, nó đọc (hoặc cố gắng) một mục từ luồng vào biến tôi đã gọi là data. Khi bạn dereference iterator, nó trả về data. Khi bạn tăng bộ lặp, nó đọc (hoặc cố gắng) mục tiếp theo từ tệp. Mặc dù được viết như thể họ so sánh một trình lặp với một trình lặp khác, operator==operator!= thực sự chỉ kiểm tra phần cuối của tệp .

Đó là sau đó được sử dụng bởi std::copy, mà (một lần nữa đơn giản hóa) trông hao hao như thế này:

template <class InIt, class OutIt> 
void std::copy(InIt b, InIt e, OutIt d) { 
    while (b != e) { 
     *d = *b; 
     ++b; 
     ++d; 
    } 
} 

Vì vậy, đây đọc và mục từ iterator đầu vào, viết mục đó vào iterator đầu ra, và lặp đi lặp lại cho đến khi iterator cho vị trí hiện tại so sánh với iterator cho phần cuối của đầu vào (điều này sẽ xảy ra khi bạn đến cuối tập tin). Lưu ý rằng không giống như các trình vòng lặp khác, vị trí "kết thúc" duy nhất mà bạn được phép sử dụng với trình lặp istream là phần cuối của tệp.


  1. Lưu ý rằng về mặt kỹ thuật, đây không phải là hành vi phù hợp. Tôi đã so sánh đơn giản để giữ mọi thứ đơn giản. Hai trình lặp lặp mặc định được xây dựng nên so sánh bằng nhau và nếu bạn xây dựng hai trình lặp từ cùng một luồng, chúng nên so sánh bằng ít nhất trước khi bạn đọc bất kỳ thứ gì từ luồng. Điều này làm cho sự khác biệt thực tế ít mặc dù - so sánh duy nhất bạn nhìn thấy trong sử dụng thực tế là để xác định xem bạn đã đạt đến cuối tập tin chưa.
+0

Các toán tử luồng có thể sao chép được không? –

+0

@quant_dev: Bạn có nghĩa là trình vòng lặp luồng không? Nếu vậy, có, nhưng không có hiệu quả nhiều. Cụ thể, bạn có thể sao chép các iterator và dereference các bản sao - nhưng ngay sau khi bạn tăng bất kỳ iterators, dereferencing bất kỳ bản sao ngoại trừ một trong những bạn chỉ tăng lên cho UB. –

5

Một phần của câu trả lời dưới đây được trích dẫn từ thư viện chuẩn C++: Một hướng dẫn và tài liệu tham khảo của Nicolai M. Josuttis với một tinh chỉnh nhỏ.

Khái niệm

istream_iterator<string>(cin) 

tạo ra một iterator chuỗi mà đọc từ dòng đầu vào tiêu chuẩn cin. Đối số mẫu string chỉ định rằng trình vòng lặp luồng đọc các phần tử thuộc loại này. Các phần tử này được đọc với toán tử nhập thông thường >>. Do đó, mỗi khi thuật toán muốn xử lý phần tử tiếp theo, trình lặp istream biến đổi mong muốn đó thành một cuộc gọi của

cin >> string 

Toán tử đầu vào cho chuỗi thường đọc một từ được phân tách bởi khoảng trắng.

Khái niệm

istream_iterator<string>() 

gọi constructor mặc định của vòng lặp istream tạo ra một cái gọi là end-of-stream iterator. Nó đại diện cho một luồng mà từ đó bạn không thể đọc được nữa. Trình lặp cuối chuỗi được sử dụng làm end of the range, do đó, thuật toán copy đọc tất cả các chuỗi từ cin cho đến khi nó không thể đọc được nữa.

cuối cùng một:

back_inserter(col)) 

theo tài liệu hướng dẫn back_inserter:

Một std :: back_insert_iterator mà có thể được sử dụng để thêm các yếu tố để kết thúc c chứa

Nó sẽ thêm tất cả đã đọc vào chuỗi thành col.

Bạn có thể tìm thông tin về std::istream_iteratorstd::back_inserter để biết chi tiết.

Các vấn đề liên quan