2012-04-03 40 views
36

Trong C++11, có hai vòng lặp trên tất cả các phần tử (phạm vi dựa trên và for_each). Có lý do nào để thích cái này hơn cái kia hay là có tình huống nào phù hợp hơn?Sử dụng tiêu chuẩn ưu tiên: phạm vi dựa trên hoặc tiêu chuẩn: for_each

for (auto& elem: container) { 
    // do something with elem 
} 

std::for_each(container.begin(), container.end(), 
       [](Elem& elem) { 
       // do something with elem 
       }); 

Ý tưởng của tôi sẽ là người đầu tiên là đơn giản và tương tự như dao vòng có trụ sở tại các ngôn ngữ khác trong khi thứ hai cũng làm việc cho chuỗi mà không phải là container đầy đủ và thứ hai là tương tự hơn để std -algorithms khác.

+0

Tôi tò mò về cách thực sự đúng cách * sử dụng * dựa trên phạm vi cho, vì vậy vui lòng xem câu hỏi của tôi http://stackoverflow.com/questions/9994789/proper-style-for-declaration-in -range-based-for – Potatoswatter

+0

Tôi đã thêm một ví dụ 'for_each' đơn giản. Tôi nghĩ tôi đã thấy một tin nhắn yêu cầu nhưng tôi không chắc nữa ... – juanchopanza

+0

@juanchopanza: Tôi đã yêu cầu một cái dựa trên lambda, nhưng tôi đã xóa nó bởi vì tại thời điểm tôi đăng bình luận, tôi đọc nhận xét của Potatoswatter rằng truy vấn một lambda là không thể. Tuy nhiên, đây là một ví dụ hay về nơi for_each có thể phù hợp hơn. – stefaanv

Trả lời

26
  1. Phạm vi dựa trên for rõ ràng là đơn giản hơn để đọc và viết. Nó là chuyên môn cho nhiệm vụ này.

    CHỈNH SỬA: Bạn có thể ngắt biểu mẫu phạm vi mà không lạm dụng ngoại lệ. (Mặc dù std::find_if thay thế cho std::for_each phép này là tốt.)

  2. std::for_each, trớ trêu thay, là giải pháp thay thế mà thực sự là phạm vi dựa và cho phép bạn chọn đặc biệt beginend giá trị thay vì toàn bộ container. (EDIT: này có thể bị hack xung quanh bằng cách sử dụng range lớp đơn giản cung cấp beginend thành viên, chẳng hạn như cung cấp bởi Boost.)

    Cũng for_each có thể thanh lịch hơn khi nếu không sử dụng chức năng bậc cao: nó có thể được sử dụng như một đối số cho bind và đối số thứ ba đã là một hàm functor.

Chủ yếu đó là vấn đề về phong cách. Hầu hết người đọc có thể thích xem for (auto &a : b) mặc dù và hầu hết các triển khai hiện hỗ trợ nó.

+0

+1 thực sự, thực tế là bạn có thể chọn phạm vi trong 'for_each' là rất mạnh mẽ. – juanchopanza

+5

@juanchopanza: tất nhiên, nếu bạn sử dụng một container 'Range' (chẳng hạn như' Boost.Range'), thì lợi thế này là tranh luận. –

+5

Bạn có thể muốn thêm rằng chỉ phạm vi cho phép ngắt và tiếp tục. – Klaim

10

std::for_each trả về hàm functor đã được sử dụng nội bộ trong vòng lặp, do đó, nó cung cấp cơ chế sạch để thu thập một số thông tin liên quan đến các phần tử trong chuỗi. Phạm vi dựa trên vòng lặp chỉ là một vòng lặp, vì vậy bất kỳ trạng thái nào được sử dụng bên ngoài vòng lặp phải được khai báo bên ngoài phạm vi đó. Trong ví dụ của bạn, nếu mục đích của các vòng lặp là để thay đổi từng phần tử của chuỗi, thì không có nhiều khác biệt chút nào. Nhưng nếu bạn không sử dụng giá trị trả về của for_each thì có lẽ bạn nên sử dụng vòng lặp đơn giản hơn. Bằng cách này, vòng lặp dựa trên phạm vi hoạt động trên các mảng kiểu C và std :: strings quá.

Đây là ví dụ về việc sử dụng giá trị trả lại là for_each, mặc dù nó không phải là một hình ảnh rất giàu trí tưởng tượng hoặc hữu ích. Nó chỉ là để minh họa cho ý tưởng.

#include <iostream> 
#include <array> 
#include <algorithm> 

struct Foo { 
    void operator()(int i) { if (i > 4) sum += i;} 
    int sum{0}; 
}; 

int main() { 

    std::array<int, 10> a{1,2,3,4,5,6,7,8,9,10}; 
    Foo foo = std::for_each(a.begin(), a.end(), Foo()); 
    std::cout << "Sum " << foo.sum << "\n"; 
} 
+1

+1, nhưng lưu ý rằng không có cách nào để truy vấn một hàm lambda functor; có được trạng thái phụ trợ sẽ được thực hiện thông qua một biến chụp, mà sẽ làm việc giống nhau trong cả hai cấu trúc. – Potatoswatter

+0

@Potatoswatter Điểm tốt. Hoặc bằng cách tạo một đối tượng hàm từ lambda. – juanchopanza

+2

@juanchopanza: Thật vậy, tình hình may mắn được cải thiện từ C++ 03 ở đây. Trong C++ 03 nó không được chỉ định nếu bạn sẽ nhận được một kết quả đầy đủ (vì các bản sao), trong khi trong C++ 11 chỉ hoạt động di chuyển được sử dụng. –

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