Có bất kỳ lợi thế của std::for_each
hơn for
vòng lặp? Đối với tôi, std::for_each
dường như chỉ cản trở khả năng đọc mã. Tại sao sau đó một số tiêu chuẩn mã hóa khuyến nghị sử dụng nó?
Có bất kỳ lợi thế của std::for_each
hơn for
vòng lặp? Đối với tôi, std::for_each
dường như chỉ cản trở khả năng đọc mã. Tại sao sau đó một số tiêu chuẩn mã hóa khuyến nghị sử dụng nó?
Câu trả lời:
Điều tuyệt vời với C ++ 11 (trước đây gọi là C ++ 0x), là cuộc tranh luận mệt mỏi này sẽ được giải quyết.
Ý tôi là, không ai trong tâm trí của họ, người muốn lặp lại toàn bộ bộ sưu tập, vẫn sẽ sử dụng cái này
for(auto it = collection.begin(); it != collection.end() ; ++it)
{
foo(*it);
}
Hoặc cái này
for_each(collection.begin(), collection.end(), [](Element& e)
{
foo(e);
});
khi cú pháp vòng lặp dựa trên phạm vifor
có sẵn:
for(Element& e : collection)
{
foo(e);
}
Loại cú pháp này đã có sẵn trong Java và C # một thời gian rồi và thực sự có nhiều foreach
vòng lặp hơn các for
vòng lặp cổ điển trong mỗi mã Java hoặc C # gần đây tôi thấy.
Element & e
khi auto & e
(hoặc auto const &e
) trông tốt hơn. Tôi sẽ sử dụng Element const e
(không có tham chiếu) khi tôi muốn chuyển đổi ngầm định, giả sử khi nguồn là tập hợp các loại khác nhau và tôi muốn chúng chuyển đổi thành Element
.
Dưới đây là một số lý do:
Nó dường như cản trở khả năng đọc chỉ vì bạn không quen với nó và / hoặc không sử dụng đúng công cụ xung quanh nó để làm cho nó thực sự dễ dàng. (xem boost :: phạm vi và boost :: bind / boost :: lambda để được trợ giúp. Nhiều trong số này sẽ đi vào C ++ 0x và làm cho for_each và các chức năng liên quan trở nên hữu ích hơn.)
Nó cho phép bạn viết một thuật toán trên đầu trang for_each hoạt động với bất kỳ trình vòng lặp nào.
Nó làm giảm cơ hội lỗi đánh máy ngu ngốc.
Nó cũng mở tâm trí của bạn với phần còn lại của STL-thuật toán, như find_if
, sort
, replace
, vv và chúng sẽ không trông rất lạ nữa. Đây có thể là một chiến thắng rất lớn.
Cập nhật 1:
Quan trọng nhất, nó giúp bạn vượt ra ngoài for_each
so với các vòng lặp như tất cả những gì có, và xem xét các STL-alog khác, như find / sort / phân vùng / copy_Vplace_if, thực thi song song .. hoặc bất cứ điều gì.
Rất nhiều quá trình xử lý có thể được viết rất chính xác bằng cách sử dụng "phần còn lại" của anh chị em của for_each, nhưng nếu tất cả những gì bạn làm là viết một vòng lặp với logic bên trong khác nhau, thì bạn sẽ không bao giờ học cách sử dụng chúng, và bạn sẽ cuối cùng phát minh ra bánh xe hơn và hơn.
Và (for_each kiểu sắp có sẵn):
for_each(monsters, boost::mem_fn(&Monster::think));
Hoặc với C ++ x11 lambdas:
for_each(monsters, [](Monster& m) { m.think(); });
IMO dễ đọc hơn:
for(Monsters::iterator i = monsters.begin(); i != monsters.end(); ++i) {
i->think();
}
Ngoài ra điều này (hoặc với lambdas, xem những người khác):
for_each(bananas, boost::bind(&Monkey::eat, my_monkey, _1));
Là ngắn gọn hơn:
for(Bananas::iterator i = bananas.begin(); i != bananas.end(); ++i) {
my_monkey->eat(*i);
}
Đặc biệt là nếu bạn có một số chức năng để gọi theo thứ tự ... nhưng có lẽ đó chỉ là tôi. ;)
Cập nhật 2 : Tôi đã viết các trình bao bọc một lớp của riêng mình các stl-algos hoạt động với các phạm vi thay vì các cặp trình vòng lặp. boost :: Range_ex, một khi được phát hành, sẽ bao gồm điều đó và có lẽ nó cũng sẽ có trong C ++ 0x?
outer_class::inner_class::iterator
hoặc chúng là các đối số mẫu: typename std::vector<T>::iterator
... chính cấu trúc đó có thể tự chạy trong một cấu trúc nhiều dòng
for_each
trong ví dụ thứ hai là không chính xác (nên làfor_each( bananas.begin(), bananas.end(),...
for_each
là chung chung hơn. Bạn có thể sử dụng nó để lặp lại trên bất kỳ loại container nào (bằng cách chuyển qua các vòng lặp bắt đầu / kết thúc). Bạn có khả năng có thể trao đổi các container bên dưới một hàm sử dụng for_each
mà không phải cập nhật mã lặp. Bạn cần xem xét rằng có những container khác trên thế giới hơn std::vector
và các mảng C cũ đơn giản để thấy những lợi thế của for_each
.
Nhược điểm chính của for_each
nó là cần một functor, vì vậy cú pháp rất khó hiểu. Điều này được cố định trong C ++ 11 (trước đây là C ++ 0x) với sự ra đời của lambdas:
std::vector<int> container;
...
std::for_each(container.begin(), container.end(), [](int& i){
i+= 10;
});
Điều này sẽ không có vẻ kỳ lạ với bạn trong 3 năm.
for ( int v : int_vector ) {
(ngay cả khi nó có thể được mô phỏng ngày hôm nay với BOOST_FOREACH)
std::for_each(container, [](int& i){ ... });
. Ý tôi là tại sao người ta buộc phải viết container hai lần?
container.each { ... }
không đề cập đến các trình lặp bắt đầu và kết thúc. Tôi thấy nó hơi dư thừa rằng tôi phải chỉ định trình lặp kết thúc mọi lúc.
Cá nhân, bất cứ lúc nào tôi cần sử dụng std::for_each
(viết hàm functor / boost::lambda
s phức tạp cho mục đích đặc biệt ), tôi tìm BOOST_FOREACH
và dựa trên phạm vi của C ++ 0x để rõ ràng hơn:
BOOST_FOREACH(Monster* m, monsters) {
if (m->has_plan())
m->act();
}
đấu với
std::for_each(monsters.begin(), monsters.end(),
if_then(bind(&Monster::has_plan, _1),
bind(&Monster::act, _1)));
Rất chủ quan, một số người sẽ nói rằng việc sử dụng for_each
sẽ làm cho mã dễ đọc hơn , vì nó cho phép xử lý các bộ sưu tập khác nhau với cùng một quy ước.
for_each
Itslef được thực hiện như một vòng lặp
template<class InputIterator, class Function>
Function for_each(InputIterator first, InputIterator last, Function f)
{
for ( ; first!=last; ++first ) f(*first);
return f;
}
Vì vậy, tùy thuộc vào bạn để chọn những gì phù hợp với bạn.
Giống như nhiều hàm thuật toán, một phản ứng ban đầu là nghĩ rằng việc sử dụng foreach không thể đọc được hơn là một vòng lặp. Đó là một chủ đề của nhiều cuộc chiến lửa.
Khi bạn đã quen với thành ngữ bạn có thể thấy nó hữu ích. Một lợi thế rõ ràng là nó buộc bộ mã hóa tách nội dung bên trong của vòng lặp khỏi chức năng lặp thực tế. (OK, tôi nghĩ đó là một lợi thế. Người khác nói rằng bạn chỉ đang cắt mã mà không có lợi ích thực sự).
Một lợi thế khác là khi tôi thấy foreach, tôi biết rằng mọi mục sẽ được xử lý hoặc một ngoại lệ sẽ bị ném.
Một vòng lặp for cho phép một số tùy chọn để kết thúc vòng lặp. Bạn có thể để vòng lặp chạy toàn bộ khóa học của mình hoặc bạn có thể sử dụng từ khóa break để nhảy ra khỏi vòng lặp hoặc sử dụng từ khóa return để thoát khỏi toàn bộ vòng lặp chức năng. Ngược lại, foreach không cho phép các tùy chọn này và điều này làm cho nó dễ đọc hơn. Bạn chỉ có thể lướt qua tên hàm và bạn biết bản chất đầy đủ của phép lặp.
Dưới đây là một ví dụ về một bối rối cho vòng lặp:
for(std::vector<widget>::iterator i = v.begin(); i != v.end(); ++i)
{
/////////////////////////////////////////////////////////////////////
// Imagine a page of code here by programmers who don't refactor
///////////////////////////////////////////////////////////////////////
if(widget->Cost < calculatedAmountSofar)
{
break;
}
////////////////////////////////////////////////////////////////////////
// And then some more code added by a stressed out juniour developer
// *#&$*)#$&#(#)$#(*$&#(&*^$#(*$#)($*#(&$^#($*&#)$(#&*$&#*$#*)$(#*
/////////////////////////////////////////////////////////////////////////
for(std::vector<widgetPart>::iterator ip = widget.GetParts().begin(); ip != widget.GetParts().end(); ++ip)
{
if(ip->IsBroken())
{
return false;
}
}
}
std::for_each()
trong tiêu chuẩn cũ (thời điểm của bài đăng này), bạn phải sử dụng một functor có tên, điều này khuyến khích khả năng đọc như bạn nói và cấm thoát ra khỏi vòng lặp sớm. Nhưng sau đó, for
vòng lặp tương đương không có gì ngoài một lệnh gọi hàm, và điều đó cũng cấm việc thoát ra sớm. Nhưng khác hơn là tôi nghĩ rằng bạn đã đưa ra một điểm tuyệt vời khi nói rằng việc std::for_each()
thực thi đi qua toàn bộ phạm vi.
Bạn hầu như đúng: hầu hết thời gian, std::for_each
là một mất mát ròng. Tôi sẽ đi xa để so sánh for_each
với goto
. goto
cung cấp điều khiển luồng linh hoạt nhất có thể - bạn có thể sử dụng nó để thực hiện hầu như bất kỳ cấu trúc điều khiển nào khác mà bạn có thể tưởng tượng. Tuy nhiên, tính linh hoạt đó có nghĩa là việc nhìn thấy một goto
sự cô lập cho bạn hầu như không có gì về những gì nó dự định làm trong tình huống này. Kết quả là, hầu như không ai trong tâm trí của họ sử dụng goto
ngoại trừ như là phương sách cuối cùng.
Trong số các thuật toán tiêu chuẩn, for_each
có nhiều cách giống nhau - nó có thể được sử dụng để thực hiện hầu như mọi thứ, điều đó có nghĩa là việc nhìn thấy for_each
cho bạn hầu như không có gì về những gì nó được sử dụng cho tình huống này. Thật không may, thái độ của mọi người đối với for_each
việc thái độ của họ đối với goto
năm 1970 như thế nào - một vài người đã bắt gặp thực tế rằng nó chỉ nên được sử dụng như là phương sách cuối cùng, nhưng nhiều người vẫn coi đó là thuật toán chính, và hiếm khi sử dụng bất kỳ khác. Phần lớn thời gian, thậm chí một cái liếc nhanh sẽ tiết lộ rằng một trong những lựa chọn thay thế là vượt trội đáng kể.
Ví dụ, tôi khá chắc chắn rằng tôi đã quên mất số lần tôi thấy mọi người viết mã để in ra nội dung của một bộ sưu tập bằng cách sử dụng for_each
. Dựa trên các bài đăng tôi đã xem, đây cũng có thể là cách sử dụng phổ biến nhất for_each
. Họ kết thúc với một cái gì đó như:
class XXX {
// ...
public:
std::ostream &print(std::ostream &os) { return os << "my data\n"; }
};
Và bài viết của họ được hỏi về những gì kết hợp bind1st
, mem_fun
vv Họ cần phải làm một cái gì đó như:
std::vector<XXX> coll;
std::for_each(coll.begin(), coll.end(), XXX::print);
làm việc và in ra các yếu tố của coll
. Nếu nó thực sự hoạt động chính xác như tôi đã viết nó ở đó, thì nó sẽ tầm thường, nhưng nó không - và đến lúc bạn làm cho nó hoạt động, thật khó để tìm thấy một vài đoạn mã liên quan đến những gì đang diễn ra giữa các mảnh giữ nó lại với nhau.
May mắn thay, có một cách tốt hơn nhiều. Thêm quá tải bộ lọc luồng bình thường cho XXX:
std::ostream &operator<<(std::ostream *os, XXX const &x) {
return x.print(os);
}
và sử dụng std::copy
:
std::copy(coll.begin(), coll.end(), std::ostream_iterator<XXX>(std::cout, "\n"));
Điều đó không làm việc - và mất hầu như không làm việc ở tất cả để tìm ra rằng nó in nội dung của coll
để std::cout
.
boost::mem_fn(&XXX::print)
hơnXXX::print
std::cout
như là đối số của nó để nó hoạt động).
Lợi thế của chức năng viết để nuôi ong dễ đọc hơn, có thể không hiển thị khi for(...)
vàfor_each(...
).
Nếu bạn sử dụng tất cả các thuật toán trong function.h, thay vì sử dụng các vòng lặp for, mã sẽ dễ đọc hơn rất nhiều;
iterator longest_tree = std::max_element(forest.begin(), forest.end(), ...);
iterator first_leaf_tree = std::find_if(forest.begin(), forest.end(), ...);
std::transform(forest.begin(), forest.end(), firewood.begin(), ...);
std::for_each(forest.begin(), forest.end(), make_plywood);
là nhiều hơn dễ đọc hơn;
Forest::iterator longest_tree = it.begin();
for (Forest::const_iterator it = forest.begin(); it != forest.end(); ++it{
if (*it > *longest_tree) {
longest_tree = it;
}
}
Forest::iterator leaf_tree = it.begin();
for (Forest::const_iterator it = forest.begin(); it != forest.end(); ++it{
if (it->type() == LEAF_TREE) {
leaf_tree = it;
break;
}
}
for (Forest::const_iterator it = forest.begin(), jt = firewood.begin();
it != forest.end();
it++, jt++) {
*jt = boost::transformtowood(*it);
}
for (Forest::const_iterator it = forest.begin(); it != forest.end(); ++it{
std::makeplywood(*it);
}
Và đó là những gì tôi nghĩ là rất hay, hãy khái quát các vòng lặp for thành một hàm dòng =)
Easy: for_each
rất hữu ích khi bạn đã có một hàm để xử lý mọi mục mảng, vì vậy bạn không phải viết lambda. Chắc chắn, cái này
for_each(a.begin(), a.end(), a_item_handler);
tốt hơn
for(auto& item: a) {
a_item_handler(a);
}
Ngoài ra, for
vòng lặp ranged chỉ lặp đi lặp lại trên toàn bộ container từ đầu đến cuối, trong khi for_each
linh hoạt hơn.
Các for_each
vòng lặp có nghĩa là để che giấu sự lặp (chi tiết về cách thức một vòng lặp được thực hiện) từ mã người dùng và xác định ngữ nghĩa rõ ràng về hoạt động: mỗi phần tử sẽ được lặp đúng một lần.
Vấn đề với khả năng đọc trong tiêu chuẩn hiện tại là nó yêu cầu functor làm đối số cuối cùng thay vì một khối mã, vì vậy trong nhiều trường hợp, bạn phải viết loại functor cụ thể cho nó. Điều đó biến thành mã ít đọc hơn vì các đối tượng functor không thể được định nghĩa tại chỗ (các lớp cục bộ được xác định trong một hàm không thể được sử dụng làm đối số khuôn mẫu) và việc thực hiện vòng lặp phải được di chuyển khỏi vòng lặp thực.
struct myfunctor {
void operator()( int arg1 ) { code }
};
void apply( std::vector<int> const & v ) {
// code
std::for_each( v.begin(), v.end(), myfunctor() );
// more code
}
Lưu ý rằng nếu bạn muốn thực hiện một thao tác cụ thể trên từng đối tượng, bạn có thể sử dụng std::mem_fn
hoặc boost::bind
( std::bind
trong tiêu chuẩn tiếp theo) hoặc boost::lambda
(lambdas trong tiêu chuẩn tiếp theo) để làm cho nó đơn giản hơn:
void function( int value );
void apply( std::vector<X> const & v ) {
// code
std::for_each( v.begin(), v.end(), boost::bind( function, _1 ) );
// code
}
Không dễ đọc và nhỏ gọn hơn phiên bản cuộn tay nếu bạn có chức năng / phương thức để gọi tại chỗ. Việc thực hiện có thể cung cấp các triển khai khác củafor_each
vòng lặp (nghĩ xử lý song song).
Tiêu chuẩn sắp tới xử lý một số thiếu sót theo các cách khác nhau, nó sẽ cho phép các lớp được định nghĩa cục bộ làm đối số cho các mẫu:
void apply( std::vector<int> const & v ) {
// code
struct myfunctor {
void operator()( int ) { code }
};
std::for_each( v.begin(), v.end(), myfunctor() );
// code
}
Cải thiện địa phương của mã: khi bạn duyệt, bạn thấy những gì nó đang làm ngay tại đó. Như một vấn đề thực tế, bạn thậm chí không cần sử dụng cú pháp lớp để định nghĩa functor, nhưng sử dụng lambda ngay tại đó:
void apply( std::vector<int> const & v ) {
// code
std::for_each( v.begin(), v.end(),
[]( int ) { // code } );
// code
}
Ngay cả trong trường hợp for_each
sẽ có một cấu trúc cụ thể sẽ làm cho nó tự nhiên hơn:
void apply( std::vector<int> const & v ) {
// code
for ( int i : v ) {
// code
}
// code
}
Tôi có xu hướng trộn các for_each
cấu trúc với các vòng cuộn tay. Khi chỉ có một cuộc gọi đến một chức năng hoặc phương thức hiện có là những gì tôi cần ( for_each( v.begin(), v.end(), boost::bind( &Type::update, _1 ) )
) tôi đi đến for_each
cấu trúc lấy đi từ mã rất nhiều công cụ lặp đĩa nồi hơi. Khi tôi cần một cái gì đó phức tạp hơn và tôi không thể thực hiện một functor chỉ một vài dòng trên mức sử dụng thực tế, tôi cuộn vòng lặp của riêng mình (giữ cho hoạt động tại chỗ). Trong các phần không quan trọng của mã, tôi có thể đi với BOOST_FOREACH (một đồng nghiệp đã đưa tôi vào đó)
Bên cạnh khả năng đọc và hiệu suất, một khía cạnh thường bị bỏ qua là tính nhất quán. Có nhiều cách để thực hiện vòng lặp for (hoặc while) trên các vòng lặp, từ:
for (C::iterator iter = c.begin(); iter != c.end(); iter++) {
do_something(*iter);
}
đến:
C::iterator iter = c.begin();
C::iterator end = c.end();
while (iter != end) {
do_something(*iter);
++iter;
}
với nhiều ví dụ ở giữa mức độ hiệu quả và tiềm năng lỗi khác nhau.
Tuy nhiên, sử dụng for_each, thực thi tính nhất quán bằng cách trừu tượng hóa vòng lặp:
for_each(c.begin(), c.end(), do_something);
Điều duy nhất bạn phải lo lắng bây giờ là: bạn có triển khai thân vòng lặp như hàm, hàm functor hoặc lambda bằng các tính năng Boost hoặc C ++ 0x không? Cá nhân, tôi khá lo lắng về điều đó hơn là cách thực hiện hoặc đọc một vòng lặp for / while ngẫu nhiên.
Tôi đã từng không thích std::for_each
và nghĩ rằng không có lambda, nó đã hoàn toàn sai. Tuy nhiên tôi đã thay đổi suy nghĩ của mình một thời gian trước đây và bây giờ tôi thực sự yêu thích nó. Và tôi nghĩ rằng nó thậm chí còn cải thiện khả năng đọc và giúp kiểm tra mã của bạn theo cách TDD dễ dàng hơn.
Các std::for_each
thuật toán có thể được đọc như làm một cái gì đó với tất cả các yếu tố trong phạm vi , mà có thể cải thiện khả năng đọc. Giả sử hành động mà bạn muốn thực hiện dài 20 dòng và chức năng thực hiện hành động cũng dài khoảng 20 dòng. Điều đó sẽ làm cho một hàm dài 40 dòng với một vòng lặp for thông thường và chỉ khoảng 20 với std::for_each
, do đó có thể dễ hiểu hơn.
Functor cho std::for_each
nhiều khả năng là chung chung hơn, và do đó có thể tái sử dụng, ví dụ:
struct DeleteElement
{
template <typename T>
void operator()(const T *ptr)
{
delete ptr;
}
};
Và trong mã, bạn chỉ có một lớp lót giống như std::for_each(v.begin(), v.end(), DeleteElement())
IMO tốt hơn một chút so với vòng lặp rõ ràng.
Tất cả các functor thường dễ dàng có được các bài kiểm tra đơn vị hơn là một vòng lặp rõ ràng ở giữa một hàm dài, và điều đó một mình đã là một chiến thắng lớn đối với tôi.
std::for_each
nhìn chung cũng đáng tin cậy hơn, vì bạn ít có khả năng phạm sai lầm với phạm vi.
Và cuối cùng, trình biên dịch có thể tạo ra mã tốt hơn một chút std::for_each
so với một số loại vòng lặp thủ công nhất định, vì nó (for_each) luôn trông giống nhau đối với trình biên dịch và các trình soạn thảo trình biên dịch có thể đưa tất cả kiến thức của họ, để làm cho nó tốt như họ có thể.
Áp dụng tương tự cho các thuật toán tiêu chuẩn khác như find_if
, transform
v.v.
for
là vòng lặp có thể lặp lại từng phần tử hoặc mỗi phần ba, v.v ... for_each
chỉ để lặp lại từng phần tử. Rõ ràng từ tên của nó. Vì vậy, rõ ràng hơn những gì bạn đang có ý định làm trong mã của bạn.
++
. Có thể không bình thường, nhưng một vòng lặp for cũng làm như vậy.
transform
để không gây nhầm lẫn cho ai đó.
Nếu bạn thường xuyên sử dụng các thuật toán khác từ STL, có một số lợi thế để for_each
:
Không giống như một vòng lặp for truyền thống, for_each
buộc bạn phải viết mã sẽ hoạt động cho bất kỳ trình lặp đầu vào nào. Bị hạn chế theo cách này thực sự có thể là một điều tốt bởi vì:
for_each
.Việc sử dụng for_each
đôi khi làm cho rõ ràng hơn rằng bạn có thể sử dụng hàm STL cụ thể hơn để làm điều tương tự. (Như trong ví dụ của Jerry Coffin; không nhất thiết phải for_each
là lựa chọn tốt nhất, nhưng vòng lặp for không phải là lựa chọn duy nhất.)
Với C ++ 11 và hai mẫu đơn giản, bạn có thể viết
for ( auto x: range(v1+4,v1+6) ) {
x*=2;
cout<< x <<' ';
}
như là một thay thế cho for_each
hoặc một vòng lặp. Tại sao chọn nó sôi sùng sục và an toàn, không có khả năng xảy ra lỗi trong một biểu thức không có ở đó.
Đối với tôi, for_each
luôn luôn tốt hơn trên cùng một lý do khi cơ thể vòng lặp đã là một functor và tôi sẽ tận dụng mọi lợi thế mà tôi có thể có được.
Bạn vẫn sử dụng biểu thức ba for
, nhưng bây giờ khi bạn nhìn thấy một biểu thức bạn biết có gì đó để hiểu ở đó, nó không phải là bản tóm tắt. Tôi ghét nồi hơi. Tôi bực bội sự tồn tại của nó. Đó không phải là mã thực, không có gì để học bằng cách đọc nó, đó chỉ là một điều nữa cần kiểm tra. Nỗ lực tinh thần có thể được đo lường bằng cách dễ dàng bị rỉ sét khi kiểm tra nó.
Các mẫu là
template<typename iter>
struct range_ {
iter begin() {return __beg;} iter end(){return __end;}
range_(iter const&beg,iter const&end) : __beg(beg),__end(end) {}
iter __beg, __end;
};
template<typename iter>
range_<iter> range(iter const &begin, iter const &end)
{ return range_<iter>(begin,end); }
Chủ yếu là bạn sẽ phải lặp đi lặp lại trên toàn bộ bộ sưu tập . Do đó, tôi khuyên bạn nên viết biến thể for_each () của riêng bạn, chỉ lấy 2 tham số. Điều này sẽ cho phép bạn viết lại ví dụ của Terry Mahaffey như:
for_each(container, [](int& i) {
i += 10;
});
Tôi nghĩ rằng điều này thực sự dễ đọc hơn một vòng lặp for. Tuy nhiên, điều này đòi hỏi các phần mở rộng trình biên dịch C ++ 0x.
Tôi thấy for_each là xấu cho khả năng đọc. Khái niệm này là một khái niệm tốt nhưng c ++ làm cho nó rất khó viết, ít nhất là đối với tôi. c ++ 0x biểu thức lamda sẽ giúp. Tôi thực sự thích ý tưởng về lamdas. Tuy nhiên, thoạt nhìn tôi nghĩ cú pháp rất xấu và tôi không chắc chắn 100% mình sẽ quen với nó. Có lẽ trong 5 năm tôi sẽ quen với nó và không cho nó một ý nghĩ thứ hai, nhưng có lẽ là không. Thời gian sẽ trả lời :)
Tôi thích sử dụng
vector<thing>::iterator istart = container.begin();
vector<thing>::iterator iend = container.end();
for(vector<thing>::iterator i = istart; i != iend; ++i) {
// Do stuff
}
Tôi tìm thấy một rõ ràng cho vòng lặp rõ ràng hơn để đọc và khám phá bằng cách sử dụng các biến được đặt tên cho các vòng lặp bắt đầu và kết thúc làm giảm sự lộn xộn trong vòng lặp for.
Tất nhiên các trường hợp khác nhau, đây chỉ là những gì tôi thường tìm thấy tốt nhất.
Bạn có thể có iterator là một cuộc gọi đến một chức năng được thực hiện trên mỗi lần lặp thông qua vòng lặp.
Xem tại đây: http://www.cplusplus.com/reference/alerskym/for_each/
for_each
, trong trường hợp đó, nó không trả lời câu hỏi về lợi thế của nó.
Đối với vòng lặp có thể phá vỡ; Tôi không muốn trở thành một con vẹt cho Herb Sutter vì vậy đây là liên kết đến bài thuyết trình của anh ấy: http://channel9.msdn.com/Events/BUILD/BUILD2011/TOOL-835T Hãy chắc chắn cũng đọc các bình luận :)
for_each
cho phép chúng tôi thực hiện mô hình Fork-Join . Khác hơn là nó hỗ trợ giao diện lưu loát .
Chúng ta có thể thêm triển khai gpu::for_each
để sử dụng cuda / gpu cho tính toán song song không đồng nhất bằng cách gọi tác vụ lambda trong nhiều công nhân.
gpu::for_each(users.begin(),users.end(),update_summary);
// all summary is complete now
// go access the user-summary here.
Và gpu::for_each
có thể đợi các công nhân làm việc trên tất cả các nhiệm vụ lambda để hoàn thành trước khi thực hiện các báo cáo tiếp theo.
Nó cho phép chúng ta viết mã có thể đọc được theo cách ngắn gọn.
accounts::erase(std::remove_if(accounts.begin(),accounts.end(),used_this_year));
std::for_each(accounts.begin(),accounts.end(),mark_dormant);
std::for_each
khi được sử dụng vớiboost.lambda
hoặcboost.bind
thường có thể cải thiện khả năng đọc