2011-06-23 37 views
13

Theo hiểu biết của tôi, hệ thống phân cấp các loại iterator đi như thế này:Các trình vòng lặp đầu vào có thể được sử dụng khi các trình vòng lặp chuyển tiếp dự kiến ​​không?

Random access -> Bi-directional -> Forward -> Input 
              -> Output 

có đúng không?

Tôi luôn nghĩ rằng có một quy tắc, nếu thuật toán mong đợi một loại trình vòng lặp cụ thể, bạn có thể cung cấp trình vòng lặp của các danh mục lên chuỗi, nhưng không phải xuống. Vì vậy, tôi đã đọc this answer, trong đó ildjarn gợi ý được đề xuất (sau đó tự sửa lại) bằng cách sử dụng std::ifstream với std::istream_iteratorstd::search để tìm dữ liệu trong một tệp. Tôi đã về để bình luận rằng bạn không thể làm điều đó, bởi vì search dự kiến ​​lặp đi lặp lại, và istream_iterator là một vòng lặp đầu vào. Nhưng chỉ để chắc chắn, tôi đã thử điều này:

std::istringstream iss("Elephant hats for sale."); 
std::istream_iterator<char> begin(iss), end; 

std::string sub("hat"); 
auto i = std::search(begin, end, sub.begin(), sub.end()); 

Tôi không mong đợi nó biên dịch, nhưng nó đã làm. Tuy nhiên, kết quả dường như vô dụng bởi vì nếu tôi theo dõi nó với điều này:

while(i != end) 
{ 
    std::cout << *i; 
    ++i; 
} 

Không có đầu ra. Vì vậy, câu hỏi của tôi là: Trình biên dịch của tôi có lỗi khi cho phép cuộc gọi của tôi đến search sử dụng istream_iterator không? Hay không có quy tắc nào ngăn cản việc này?

+0

Vì lỗi lầm rõ ràng của tôi; -] Tôi đã cập nhật câu trả lời của mình bằng các liên kết đến trình bao bọc (từ Boost.Spirit) cho các trình lặp đầu vào làm cho chúng thích hợp để sử dụng như các trình vòng lặp chuyển tiếp. – ildjarn

Trả lời

17

Các trình vòng lặp đầu vào có thể được sử dụng khi các trình vòng lặp chuyển tiếp dự kiến ​​không?

No. Sự khác biệt giữa trình lặp đầu vào và trình chuyển tiếp chuyển tiếp là trình lặp đầu vào là trình lặp lặp "một lần" nhưng trình chuyển tiếp chuyển tiếp là trình lặp "nhiều lần".

Khi bạn nâng cấp trình lặp đầu vào, bạn không còn có thể truy cập các phần tử trước đó trong phạm vi. Nếu bạn tạo một bản sao của một trình lặp đầu vào, cả hai trình vòng lặp vẫn hợp lệ cho đến khi bạn nâng cấp một trong số chúng; thì người kia chấm dứt hợp lệ. Với một iterator chuyển tiếp, bạn có thể lặp qua chuỗi bất kỳ số lần nào, bạn có thể có nhiều bản sao có thể sử dụng của một trình lặp cùng một lúc, bạn có thể sử dụng nhiều trình lặp vào chuỗi cùng một lúc, và bạn có thể dereference một lặp lại nhiều lần như bạn muốn trước khi tiến hành lại lần nữa.

Vì vậy, câu hỏi của tôi là: Trình biên dịch của tôi có lỗi khi cho phép cuộc gọi của tôi tìm kiếm bằng cách sử dụng istream_iterator không?

Không có quy tắc nào cho trình biên dịch phải từ chối mã.

Quy tắc là bạn phải chắc chắn chuyển đúng loại trình lặp được yêu cầu bởi hàm. Đôi khi nếu bạn vượt qua sai loại iterator bạn nhận được một lỗi biên dịch. Đôi khi chương trình sẽ biên dịch nhưng sẽ không hoạt động chính xác. Đôi khi mọi thứ sẽ xuất hiện để hoạt động chính xác. Kết quả là không xác định nếu bạn vi phạm các yêu cầu gọi hàm.


Thuật toán chung thường áp đặt các yêu cầu về thông số loại của chúng bằng cách giả định rằng đối số kiểu được cung cấp thực sự đáp ứng yêu cầu. Vì vậy, ví dụ: thuật toán chỉ hoạt động với trình vòng lặp truy cập ngẫu nhiên sẽ "thực thi" yêu cầu này bằng cách thực hiện một số thao tác chỉ hoạt động với trình vòng lặp truy cập ngẫu nhiên (ví dụ: it + 1).Nếu trình vòng lặp không hỗ trợ thao tác đó (operator+(iterator, int) tại đây), mã sẽ không biên dịch được.

Vấn đề là không có cách nào để phân biệt giữa vòng lặp đầu vào và trình vòng lặp chuyển tiếp theo cách này: bạn có thể tăng và dereference cả hai; sự khác biệt là số lần bạn có thể thực hiện từng thao tác và chuỗi trong đó bạn có thể thực hiện các thao tác đó. Vì vậy, một thuật toán như std::search sẽ sử dụng *it++it, sẽ "hoạt động" tốt cho các trình lặp đầu vào, ít nhất là khi mã sẽ biên dịch.

Về lý thuyết, thuật toán có thể sử dụng mẫu lớp std::iterator_traits để xác định liệu trình lặp là một trình lặp đầu vào hay trình lặp chuyển tiếp; Tôi không biết liệu điều đó có được phép theo tiêu chuẩn ngôn ngữ C++ hay không. Nếu thư viện đã làm điều đó, bạn có thể gặp lỗi biên dịch mã của bạn, điều này sẽ tốt hơn.

+0

Vì vậy, đó là hành vi không xác định, tôi nên có suy nghĩ về điều đó. Cảm ơn James. –

+0

"* Tôi không biết liệu điều đó có được phép theo tiêu chuẩn ngôn ngữ C++ không. *" Tại sao nó không được? Không từ chối biên dịch một biểu hiện hợp lệ của UB? – ildjarn

+2

@ildjarn: Tôi không biết liệu nó có yêu cầu một kiểu có đúng kiểu «iterator_category' typedef để đáp ứng các khái niệm của trình lặp hay không. Tôi sẽ nghĩ rằng _would_ được yêu cầu, nhưng không libstdC++ cũng như VC++ thực hiện lợi dụng điều đó, vì vậy tôi không chắc chắn. –

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