มีข้อดีของการใด ๆstd::for_each
มากกว่าfor
ห่วง? สำหรับฉันstd::for_each
ดูเหมือนว่าจะเป็นอุปสรรคต่อการอ่านรหัสเท่านั้น ทำไมมาตรฐานการเข้ารหัสบางอย่างแนะนำให้ใช้?
มีข้อดีของการใด ๆstd::for_each
มากกว่าfor
ห่วง? สำหรับฉันstd::for_each
ดูเหมือนว่าจะเป็นอุปสรรคต่อการอ่านรหัสเท่านั้น ทำไมมาตรฐานการเข้ารหัสบางอย่างแนะนำให้ใช้?
คำตอบ:
สิ่งที่ดีกับC ++ 11 (ก่อนหน้านี้เรียกว่า C ++ 0x) คือการอภิปรายที่น่าเบื่อนี้จะถูกตัดสิน
ฉันหมายความว่าไม่มีใครในใจที่ถูกต้องของพวกเขาที่ต้องการจะย้ำกับคอลเลกชันทั้งหมดจะยังคงใช้มัน
for(auto it = collection.begin(); it != collection.end() ; ++it)
{
foo(*it);
}
หรือสิ่งนี้
for_each(collection.begin(), collection.end(), [](Element& e)
{
foo(e);
});
เมื่อไวยากรณ์วนลูปตามช่วงfor
พร้อมใช้งาน:
for(Element& e : collection)
{
foo(e);
}
ไวยากรณ์ชนิดนี้มีอยู่ใน Java และ C # ในบางเวลาและที่จริงมีforeach
ลูปมากกว่าfor
ลูปแบบคลาสสิกในโค้ด Java หรือ C # ทุกครั้งที่ฉันเห็น
Element & e
ในขณะที่ auto & e
(หรือauto const &e
) ดูดีขึ้น ฉันต้องการใช้Element const e
(โดยไม่มีการอ้างอิง) Element
เมื่อฉันต้องการแปลงนัยกล่าวว่าเมื่อมาเป็นคอลเลกชันของประเภทที่แตกต่างกันและฉันต้องการให้พวกเขาแปลงเป็น
นี่คือสาเหตุบางประการ:
ดูเหมือนว่าจะเป็นอุปสรรคต่อการอ่านเพียงเพราะคุณไม่คุ้นเคยกับมันและ / หรือไม่ได้ใช้เครื่องมือที่เหมาะสมเพื่อทำให้มันง่าย (ดูที่บูสเตอร์ :: range :: boost และ :: :: bind / boost :: lambda สำหรับผู้ช่วยเหลือเหล่านี้จะเป็น C ++ 0x และทำให้ for_each และฟังก์ชั่นที่เกี่ยวข้องมีประโยชน์มากกว่า)
อนุญาตให้คุณเขียนอัลกอริทึมที่ด้านบนของ for_each ที่ทำงานร่วมกับตัววนซ้ำใดก็ได้
มันลดโอกาสของการพิมพ์บักโง่
นอกจากนี้ยังเปิดใจของคุณส่วนที่เหลือของ STL-ขั้นตอนวิธีการเช่นfind_if
, sort
, replace
ฯลฯ และเหล่านี้จะไม่ดูแปลกอีกต่อไปดังนั้น นี่อาจเป็นชัยชนะครั้งใหญ่
อัปเดต 1:
ที่สำคัญที่สุดคือช่วยให้คุณก้าวไปข้างหน้าfor_each
เทียบกับ for-loops อย่างที่มีอยู่ทั้งหมดและดู STL-alogs อื่น ๆ เช่น find / sort / partition / copy_replace_if, การประมวลผลแบบขนาน .. หรืออะไรก็ตาม
การประมวลผลจำนวนมากสามารถเขียนได้อย่างกระชับโดยใช้ "ส่วนที่เหลือ" ของพี่น้องของ for_each แต่ถ้าสิ่งที่คุณทำคือการเขียน for-loop ด้วยตรรกะภายในที่หลากหลายคุณจะไม่มีทางเรียนรู้วิธีใช้สิ่งเหล่านั้น จบลงด้วยการคิดค้นล้อซ้ำแล้วซ้ำอีก
และ (สำหรับช่วงสไตล์เร็ว ๆ นี้สำหรับ for_each):
for_each(monsters, boost::mem_fn(&Monster::think));
หรือกับ lambdas C ++ x11:
for_each(monsters, [](Monster& m) { m.think(); });
IMO สามารถอ่านได้มากกว่า:
for(Monsters::iterator i = monsters.begin(); i != monsters.end(); ++i) {
i->think();
}
เช่นนี้ (หรือกับ lambdas, เห็นคนอื่น ๆ ):
for_each(bananas, boost::bind(&Monkey::eat, my_monkey, _1));
กระชับกว่า:
for(Bananas::iterator i = bananas.begin(); i != bananas.end(); ++i) {
my_monkey->eat(*i);
}
โดยเฉพาะอย่างยิ่งถ้าคุณมีฟังก์ชั่นการโทรที่หลากหลายตามลำดับ ... แต่บางทีนั่นอาจเป็นฉัน ;)
อัปเดต 2 : ฉันได้เขียน wraps หนึ่งซับของ stl-algos ที่ทำงานกับช่วงแทนตัววนซ้ำคู่หนึ่ง เพิ่ม :: range_ex เมื่อเปิดตัวจะรวมอยู่ในนั้นและอาจจะมีใน C ++ 0x ด้วยหรือไม่
outer_class::inner_class::iterator
หรือพวกเขาเป็นข้อโต้แย้งแม่แบบ: typename std::vector<T>::iterator
... สำหรับการสร้างตัวเองสามารถทำงานเป็นสายการสร้างจำนวนมากในตัวเอง
for_each
ตัวอย่างที่สองไม่ถูกต้อง (ควรfor_each( bananas.begin(), bananas.end(),...
for_each
เป็นเรื่องทั่วไปมากขึ้น คุณสามารถใช้มันเพื่อวนซ้ำคอนเทนเนอร์ทุกประเภท (โดยผ่านในตัววนเริ่มต้น / สิ้นสุด) คุณสามารถสลับคอนเทนเนอร์ใต้ฟังก์ชันที่ใช้for_each
โดยไม่ต้องอัปเดตรหัสซ้ำ คุณต้องพิจารณาว่ามีภาชนะอื่น ๆ ในโลกกว่าstd::vector
และธรรมดาอาร์เรย์ C for_each
เก่าที่จะเห็นประโยชน์ของ
ข้อเสียเปรียบหลักของfor_each
มันคือการใช้ functor ดังนั้นไวยากรณ์เป็น clunky สิ่งนี้ได้รับการแก้ไขใน C ++ 11 (เดิมคือ C ++ 0x) โดยมีการแนะนำ lambdas:
std::vector<int> container;
...
std::for_each(container.begin(), container.end(), [](int& i){
i+= 10;
});
นี้จะไม่ดูแปลกสำหรับคุณใน 3 ปี
for ( int v : int_vector ) {
(แม้ว่าจะสามารถจำลองวันนี้ด้วย BOOST_FOREACH)
std::for_each(container, [](int& i){ ... });
. ฉันหมายถึงเหตุใดจึงถูกบังคับให้เขียนคอนเทนเนอร์สองครั้ง
container.each { ... }
โดยไม่ต้องกล่าวถึงตัวเริ่มต้นและสิ้นสุด ฉันพบว่ามันซ้ำซ้อนเล็กน้อยที่ฉันต้องระบุตัววนซ้ำสุดท้ายตลอดเวลา
โดยส่วนตัวเมื่อใดก็ตามที่ฉันต้องออกไปใช้std::for_each
(เขียนฟังก์ชั่นวัตถุประสงค์พิเศษ / ซับซ้อนboost::lambda
s) ฉันจะหาBOOST_FOREACH
และใช้ช่วง C + 0x เพื่อความชัดเจนมากขึ้น:
BOOST_FOREACH(Monster* m, monsters) {
if (m->has_plan())
m->act();
}
VS
std::for_each(monsters.begin(), monsters.end(),
if_then(bind(&Monster::has_plan, _1),
bind(&Monster::act, _1)));
มันเป็นอัตนัยมากบางคนบอกว่าการใช้for_each
จะทำให้โค้ดอ่านง่ายขึ้นเนื่องจากช่วยให้สามารถปฏิบัติต่อคอลเลกชันที่แตกต่างกันด้วยแบบแผนเดียวกัน
for_each
itslef มีการใช้งานเป็นวง
template<class InputIterator, class Function>
Function for_each(InputIterator first, InputIterator last, Function f)
{
for ( ; first!=last; ++first ) f(*first);
return f;
}
ดังนั้นขึ้นอยู่กับคุณที่จะเลือกสิ่งที่ถูกต้องสำหรับคุณ
เช่นเดียวกับฟังก์ชั่นอัลกอริธึมปฏิกิริยาแรกคือการคิดว่าไม่สามารถอ่าน foreach ได้มากกว่าการวนซ้ำ มันเป็นหัวข้อของสงครามเพลิงหลายครั้ง
เมื่อคุณคุ้นเคยกับสำนวนแล้วคุณอาจพบว่ามีประโยชน์ ข้อดีอย่างหนึ่งที่เห็นได้ชัดคือมันบังคับให้ coder แยกเนื้อหาด้านในของลูปออกจากฟังก์ชันการทำซ้ำตามจริง (ตกลงฉันคิดว่ามันเป็นข้อได้เปรียบอื่น ๆ บอกว่าคุณเพิ่งสับโค้ดโดยไม่มีผลประโยชน์จริง)
ข้อดีอีกอย่างหนึ่งคือเมื่อเห็น foreach ฉันรู้ว่าทุกรายการจะถูกประมวลผลหรือมีข้อยกเว้นจะถูกโยน
สำหรับห่วงช่วยให้หลายตัวเลือกสำหรับการยกเลิกห่วง คุณสามารถปล่อยให้วงวนทำงานเต็มหลักสูตรหรือคุณสามารถใช้คีย์เวิร์ดbreakเพื่อกระโดดออกจากลูปอย่างชัดเจนหรือใช้คีย์เวิร์ดreturnเพื่อออกจากฟังก์ชันมิดลูปทั้งหมด ในทางตรงกันข้ามforeachไม่อนุญาตให้ใช้ตัวเลือกเหล่านี้และทำให้อ่านได้ง่ายขึ้น คุณสามารถมองไปที่ชื่อฟังก์ชั่นและคุณก็รู้ถึงลักษณะของการวนซ้ำ
นี่คือตัวอย่างของความสับสนสำหรับลูป:
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()
ในมาตรฐานเก่า (ในช่วงเวลาของโพสต์นี้) คุณจะต้องใช้ functor ที่มีชื่อซึ่งจะส่งเสริมให้อ่านง่ายในขณะที่คุณพูดและห้ามการแตกออกจากวงก่อนกำหนด แต่จากนั้นfor
ลูปที่เทียบเท่าจะไม่มีอะไรเลยนอกจากการเรียกใช้ฟังก์ชั่น แต่นอกเหนือจากนั้นฉันคิดว่าคุณสร้างจุดที่ยอดเยี่ยมในการบอกว่าstd::for_each()
บังคับให้ผ่านช่วงทั้งหมด
คุณถูกต้องส่วนใหญ่แล้วส่วนใหญ่แล้วstd::for_each
เป็นผลขาดทุนสุทธิ ฉันต้องการไปให้ไกลที่สุดเท่าที่จะเปรียบเทียบไปfor_each
มอบการควบคุมการไหลที่หลากหลายที่สุดเท่าที่จะเป็นไปได้ - คุณสามารถใช้มันเพื่อสร้างโครงสร้างการควบคุมอื่น ๆ ที่คุณสามารถจินตนาการได้ อย่างไรก็ตามความสามารถรอบด้านนั้นหมายถึงการมองเห็นสิ่งที่แยกจากกันไม่ได้บอกอะไรคุณเกี่ยวกับสิ่งที่ตั้งใจทำในสถานการณ์นี้ เป็นผลให้ไม่มีใครในใจที่ถูกต้องใช้ยกเว้นเป็นวิธีสุดท้ายgoto
goto
goto
goto
ในบรรดาอัลกอริธึมมาตรฐานนั้นfor_each
ก็เป็นวิธีเดียวกัน - สามารถใช้เพื่อนำไปใช้งานได้จริงซึ่งหมายความว่าการเห็นfor_each
จะไม่บอกอะไรเกี่ยวกับสิ่งที่มันถูกใช้ในสถานการณ์นี้ น่าเสียดายที่ทัศนคติของผู้คนต่อfor_each
เรื่องที่ว่าทัศนคติของพวกเขามีต่อgoto
ในปี 1970 หรือมากกว่านั้นมีไม่กี่คนที่ติดอยู่กับความจริงที่ว่ามันควรจะใช้เป็นทางเลือกสุดท้ายเท่านั้น แต่หลายคนยังคงคิดว่ามันเป็น ไม่ค่อยมีใครใช้เลย เวลาส่วนใหญ่แม้จะมองอย่างรวดเร็วก็จะแสดงให้เห็นว่าหนึ่งในทางเลือกนั้นเหนือกว่าอย่างมาก
for_each
เพียงแค่ยกตัวอย่างเช่นผมค่อนข้างมั่นใจว่าฉันเสียติดตามกี่ครั้งที่ผมเคยเห็นคนเขียนโค้ดที่จะพิมพ์ออกเนื้อหาของคอลเลกชันโดยใช้ for_each
ขึ้นอยู่กับการโพสต์ที่ผมเคยเห็นนี้อาจจะดีการใช้งานทั่วไปมากที่สุดคนหนึ่งของ พวกเขาจบลงด้วยสิ่งที่ชอบ:
class XXX {
// ...
public:
std::ostream &print(std::ostream &os) { return os << "my data\n"; }
};
และการโพสต์ของพวกเขาจะถามเกี่ยวกับสิ่งที่รวมกันของbind1st
, mem_fun
ฯลฯ พวกเขาต้องการที่จะทำให้สิ่งที่ชอบ:
std::vector<XXX> coll;
std::for_each(coll.begin(), coll.end(), XXX::print);
coll
ทำงานและพิมพ์ออกมาองค์ประกอบของ ถ้ามันใช้งานได้จริงอย่างที่ฉันเขียนไว้ตรงนั้นมันน่าจะปานกลาง แต่ก็ไม่ได้ - และเมื่อคุณได้มันมาทำงานมันยากที่จะหารหัสสองสามบิตที่เกี่ยวข้องกับสิ่งที่ เกิดขึ้นท่ามกลางชิ้นส่วนที่ถือมันไว้ด้วยกัน
โชคดีที่มีวิธีที่ดีกว่ามาก เพิ่มตัวแทรกตัวแทรกกระแสปกติสำหรับ XXX:
std::ostream &operator<<(std::ostream *os, XXX const &x) {
return x.print(os);
}
และใช้std::copy
:
std::copy(coll.begin(), coll.end(), std::ostream_iterator<XXX>(std::cout, "\n"));
ที่ทำงาน - และใช้เวลาจริงการทำงานที่ไม่ทั้งหมดที่จะคิดออกว่าจะพิมพ์เนื้อหาของการcoll
std::cout
boost::mem_fn(&XXX::print)
มากกว่าXXX::print
std::cout
เป็นอาร์กิวเมนต์เพื่อให้ทำงาน)
ข้อดีของการเขียนฟังก์ชั่นสำหรับ beeing ที่อ่านได้มากขึ้นอาจไม่ปรากฏขึ้นเมื่อใดfor(...)
และfor_each(...
)
หากคุณใช้อัลกอริธึมทั้งหมดใน functional.h แทนที่จะใช้ for-ลูปรหัสจะอ่านง่ายขึ้นมาก
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);
เป็นมากเพิ่มเติมอ่านได้กว่า;
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);
}
และนั่นคือสิ่งที่ฉันคิดว่าเป็นสิ่งที่ดีมากพูดคุยกับ for-loops กับฟังก์ชันหนึ่งบรรทัด =)
ง่าย ๆ : for_each
มีประโยชน์เมื่อคุณมีฟังก์ชั่นในการจัดการทุกรายการอาเรย์ดังนั้นคุณไม่จำเป็นต้องเขียนแลมบ์ดา แน่นอนว่าสิ่งนี้
for_each(a.begin(), a.end(), a_item_handler);
ดีกว่า
for(auto& item: a) {
a_item_handler(a);
}
นอกจากนี้การfor
วนซ้ำจะวนซ้ำวนซ้ำตั้งแต่ต้นจนจบเท่านั้นและfor_each
มีความยืดหยุ่นมากกว่า
การfor_each
วนซ้ำนั้นหมายถึงการซ่อนตัววนซ้ำ (รายละเอียดของการวนซ้ำ) จากรหัสผู้ใช้และกำหนดความหมายที่ชัดเจนเกี่ยวกับการดำเนินการ: แต่ละองค์ประกอบจะถูกวนซ้ำทันที
ปัญหาเกี่ยวกับความสามารถในการอ่านในมาตรฐานปัจจุบันคือต้องใช้ functor เป็นอาร์กิวเมนต์สุดท้ายแทนที่จะเป็นบล็อคของรหัสดังนั้นในหลายกรณีคุณต้องเขียนประเภท functor เฉพาะสำหรับมัน ทำให้กลายเป็นรหัสที่อ่านได้น้อยลงเนื่องจากไม่สามารถกำหนดวัตถุ functor ในสถานที่ (คลาสท้องถิ่นที่กำหนดภายในฟังก์ชั่นไม่สามารถใช้เป็นอาร์กิวเมนต์ของเทมเพลต) และการดำเนินการของลูปจะต้องย้ายออกจากลูปจริง
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
}
โปรดทราบว่าหากคุณต้องการดำเนินการเฉพาะกับแต่ละวัตถุคุณสามารถใช้std::mem_fn
หรือboost::bind
( std::bind
ในมาตรฐานถัดไป) หรือboost::lambda
(lambdas ในมาตรฐานถัดไป) เพื่อให้ง่ายขึ้น:
void function( int value );
void apply( std::vector<X> const & v ) {
// code
std::for_each( v.begin(), v.end(), boost::bind( function, _1 ) );
// code
}
ซึ่งไม่สามารถอ่านได้น้อยและกะทัดรัดกว่ารุ่นรีดมือถ้าคุณมีฟังก์ชั่น / วิธีการโทรเข้าที่ การใช้งานสามารถให้การใช้งานอื่น ๆ ของfor_each
วง (คิดว่าการประมวลผลแบบขนาน)
มาตรฐานที่กำลังจะมาถึงจะดูแลข้อบกพร่องบางอย่างในรูปแบบที่แตกต่างกันซึ่งจะอนุญาตให้คลาสที่กำหนดแบบโลคัลเป็นอาร์กิวเมนต์สำหรับเทมเพลต:
void apply( std::vector<int> const & v ) {
// code
struct myfunctor {
void operator()( int ) { code }
};
std::for_each( v.begin(), v.end(), myfunctor() );
// code
}
การปรับปรุงตำแหน่งของรหัส: เมื่อคุณเรียกดูคุณจะเห็นสิ่งที่กำลังทำอยู่ที่นั่น ตามความเป็นจริงคุณไม่จำเป็นต้องใช้ไวยากรณ์ของคลาสเพื่อกำหนด functor แต่ใช้แลมบ์ดาตรงนั้น:
void apply( std::vector<int> const & v ) {
// code
std::for_each( v.begin(), v.end(),
[]( int ) { // code } );
// code
}
แม้ว่าในกรณีของfor_each
จะมีโครงสร้างเฉพาะที่จะทำให้เป็นธรรมชาติมากขึ้น:
void apply( std::vector<int> const & v ) {
// code
for ( int i : v ) {
// code
}
// code
}
ฉันมักจะผสมสิ่งfor_each
ก่อสร้างกับลูปที่รีดด้วยมือ เมื่อมีเพียงการเรียกไปยังฟังก์ชันหรือเมธอดที่มีอยู่คือสิ่งที่ฉันต้องการ ( for_each( v.begin(), v.end(), boost::bind( &Type::update, _1 ) )
) ฉันจะไปที่สิ่งfor_each
ก่อสร้างที่นำรหัสออกไปจากหม้อไอน้ำจำนวนมากจำนวนมาก เมื่อฉันต้องการบางสิ่งที่ซับซ้อนมากขึ้นและฉันไม่สามารถใช้ functor เพียงไม่กี่บรรทัดเหนือการใช้งานจริงฉันจะหมุนวนของตัวเอง ในส่วนที่ไม่สำคัญของรหัสฉันอาจไปกับ BOOST_FOREACH (เพื่อนร่วมงานพาฉันเข้าไป)
นอกเหนือจากความสามารถในการอ่านและประสิทธิภาพแล้วแง่มุมหนึ่งที่มักถูกมองข้ามก็คือความสม่ำเสมอ มีหลายวิธีในการใช้ลูป for (หรือ while) บนตัววนซ้ำจาก:
for (C::iterator iter = c.begin(); iter != c.end(); iter++) {
do_something(*iter);
}
ถึง:
C::iterator iter = c.begin();
C::iterator end = c.end();
while (iter != end) {
do_something(*iter);
++iter;
}
ด้วยตัวอย่างมากมายในระหว่างที่ระดับต่าง ๆ ของประสิทธิภาพและศักยภาพแมลง
อย่างไรก็ตามการใช้ for_each บังคับให้เกิดความสม่ำเสมอโดยการวนลูปออก:
for_each(c.begin(), c.end(), do_something);
สิ่งเดียวที่คุณต้องกังวลในตอนนี้คือ: คุณใช้ร่างกายวนเป็นฟังก์ชั่น, functor หรือแลมบ์ดาที่ใช้คุณสมบัติ Boost หรือ C ++ 0x? โดยส่วนตัวฉันค่อนข้างกังวลเกี่ยวกับเรื่องนั้นมากกว่าวิธีการนำไปใช้หรืออ่านแบบสุ่มสำหรับ / ในขณะที่วนซ้ำ
ฉันเคยเกลียดstd::for_each
และคิดว่าถ้าปราศจากแลมบ์ดามันทำผิดไปอย่างสิ้นเชิง อย่างไรก็ตามฉันได้เปลี่ยนใจเมื่อไม่นานมานี้และตอนนี้ฉันรักมันจริง ๆ และฉันคิดว่ามันปรับปรุงการอ่านได้ง่ายขึ้นและทำให้ง่ายขึ้นในการทดสอบรหัสของคุณในแบบ TDD
std::for_each
อัลกอริทึมสามารถอ่านได้บางสิ่งบางอย่างที่ต้องทำกับองค์ประกอบทั้งหมดในช่วงที่สามารถปรับปรุงการอ่าน สมมติว่าการกระทำที่คุณต้องการดำเนินการนั้นมีความยาว 20 บรรทัดและฟังก์ชันที่การดำเนินการนั้นมีความยาวประมาณ 20 บรรทัด นั่นจะทำให้ฟังก์ชั่น 40 บรรทัดมีความยาวแบบธรรมดาสำหรับลูปและเพียงประมาณ 20 ด้วยstd::for_each
จึงน่าจะง่ายต่อการเข้าใจ
std::for_each
มีแนวโน้มที่จะเป็นคนธรรมดามากขึ้นสำหรับ Functors และนำมาใช้ใหม่เช่น:
struct DeleteElement
{
template <typename T>
void operator()(const T *ptr)
{
delete ptr;
}
};
และในรหัสที่คุณมีเพียงหนึ่งซับstd::for_each(v.begin(), v.end(), DeleteElement())
ที่ IMO ดีกว่าเล็กน้อยอย่างชัดเจนห่วง
โดยทั่วไปแล้วฟังก์ชั่นทั้งหมดนั้นง่ายกว่าที่จะทดสอบภายใต้หน่วยมากกว่าการวนซ้ำในช่วงกลางของฟังก์ชั่นที่ยาวนานและการชนะเพียงครั้งเดียวก็เป็นเรื่องใหญ่สำหรับฉัน
std::for_each
โดยทั่วไปก็มีความน่าเชื่อถือมากกว่าเนื่องจากคุณมีโอกาสน้อยที่จะทำผิดพลาดกับช่วง
และท้ายที่สุดคอมไพเลอร์อาจสร้างโค้ดที่ดีกว่าเล็กน้อยstd::for_each
สำหรับบางประเภทของงานฝีมือที่ทำด้วยมือสำหรับลูปเนื่องจาก (for_each) มักจะดูเหมือนกันสำหรับคอมไพเลอร์และผู้เขียนคอมไพเลอร์สามารถใส่ความรู้ทั้งหมดเพื่อให้ดีที่สุด สามารถ.
เช่นเดียวกับขั้นตอนวิธีการมาตรฐานอื่น ๆ เช่นfind_if
, transform
ฯลฯ
for
สำหรับลูปที่สามารถวนซ้ำแต่ละองค์ประกอบหรือทุก ๆ สามเป็นต้นfor_each
สำหรับวนซ้ำแต่ละองค์ประกอบเท่านั้น ชัดเจนจากชื่อของมัน ดังนั้นชัดเจนว่าคุณตั้งใจจะทำอะไรในโค้ดของคุณ
++
กับแต่ละ อาจจะผิดปกติ แต่สำหรับ for-loop ก็ทำเช่นเดียวกัน
transform
เพื่อไม่ให้ใครสับสน
หากคุณใช้อัลกอริธึมอื่นจาก STL บ่อยครั้งมีข้อดีหลายประการfor_each
ดังนี้:
ซึ่งแตกต่างจากแบบดั้งเดิมสำหรับวงfor_each
บังคับให้คุณเขียนรหัสที่จะทำงานสำหรับ iterator การป้อนข้อมูลใด ๆ การถูก จำกัด ด้วยวิธีนี้อาจเป็นสิ่งที่ดีเพราะ:
for_each
ที่ปรับเปลี่ยนรหัสเดียวกันอาจจะไม่ทำเช่นนี้โดยไม่ต้องได้รับแจ้งให้ใช้for_each
บางครั้งการใช้ทำให้ชัดเจนมากขึ้นว่าคุณสามารถใช้ฟังก์ชัน STL ที่เฉพาะเจาะจงมากขึ้นเพื่อทำสิ่งเดียวกัน (เช่นเดียวกับตัวอย่างของ Jerry Coffin มันไม่จำเป็นว่าfor_each
เป็นตัวเลือกที่ดีที่สุด แต่สำหรับลูปไม่ใช่ทางเลือกเดียวเท่านั้น)
ด้วย C ++ 11 และสองแม่แบบง่าย ๆ คุณสามารถเขียน
for ( auto x: range(v1+4,v1+6) ) {
x*=2;
cout<< x <<' ';
}
เพื่อทดแทนfor_each
หรือห่วง เหตุใดจึงเลือกที่จะลดความกะทัดรัดและความปลอดภัยลงจึงไม่มีโอกาสผิดพลาดในการแสดงออกที่ไม่มีอยู่
สำหรับฉันfor_each
ดีกว่าเสมอในบริเวณเดียวกันเมื่อร่างกายลูปเป็นนักแสดงแล้วและฉันจะใช้ประโยชน์จากสิ่งที่ฉันได้รับ
คุณยังคงใช้สามนิพจน์for
แต่ตอนนี้เมื่อคุณเห็นสิ่งที่คุณรู้ว่ามีบางสิ่งที่จะเข้าใจมีมันไม่ได้สำเร็จรูป ฉันเกลียดหม้อไอน้ำ ฉันไม่พอใจการดำรงอยู่ของมัน ไม่ใช่รหัสจริงไม่มีอะไรให้เรียนรู้โดยการอ่านมันเป็นอีกสิ่งหนึ่งที่ต้องตรวจสอบ ความพยายามทางจิตสามารถวัดได้โดยง่ายเพียงวิธีการเป็นสนิมในการตรวจสอบ
แม่แบบคือ
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); }
ส่วนใหญ่คุณจะต้องย้ำกว่าคอลเลกชันทั้งหมด ดังนั้นฉันขอแนะนำให้คุณเขียนตัวแปร for_each () ของคุณเองโดยรับเพียง 2 พารามิเตอร์ สิ่งนี้จะช่วยให้คุณเขียนตัวอย่างของ Terry Mahaffeyเป็น:
for_each(container, [](int& i) {
i += 10;
});
ฉันคิดว่านี่สามารถอ่านได้มากกว่าการวนซ้ำ อย่างไรก็ตามสิ่งนี้ต้องการส่วนขยายคอมไพเลอร์ C ++ 0x
ฉันคิดว่า for_each นั้นไม่ดีสำหรับการอ่านได้ แนวคิดนี้ดี แต่ c ++ ทำให้ยากต่อการเขียนอ่านอย่างน้อยสำหรับฉัน การแสดงออก lamda c ++ 0x จะช่วย ฉันชอบความคิดของลำดาส อย่างไรก็ตามในครั้งแรกที่ฉันคิดว่าไวยากรณ์น่าเกลียดมากและฉันไม่แน่ใจ 100% ว่าฉันจะชินกับมัน บางทีใน 5 ปีฉันจะชินกับมันและไม่ให้ความคิดที่สอง แต่อาจจะไม่ เวลาจะบอกเอง :)
ฉันชอบที่จะใช้
vector<thing>::iterator istart = container.begin();
vector<thing>::iterator iend = container.end();
for(vector<thing>::iterator i = istart; i != iend; ++i) {
// Do stuff
}
ฉันพบความชัดเจนในการวนลูปที่ชัดเจนขึ้นในการอ่านและการอธิบายโดยใช้ตัวแปรที่กำหนดชื่อสำหรับตัววนซ้ำเริ่มต้นและจุดสิ้นสุดลดความยุ่งเหยิงในลูป for
แน่นอนกรณีแตกต่างกันไปนี่เป็นเพียงสิ่งที่ฉันมักจะหาที่ดีที่สุด
คุณสามารถให้ตัววนซ้ำเป็นสายไปยังฟังก์ชันที่ดำเนินการในแต่ละรอบซ้ำผ่านการวนซ้ำ
ดูที่นี่: http://www.cplusplus.com/reference/algorithm/for_each/
for_each
ทำในกรณีนี้มันไม่ตอบคำถามเกี่ยวกับข้อดีของมัน
สำหรับวงสามารถทำลาย; ฉันไม่ต้องการที่จะเป็นนกแก้วสำหรับ Herb Sutter ดังนั้นนี่คือลิงก์ไปยังงานนำเสนอของเขา: http://channel9.msdn.com/Events/BUILD/BUILD2011/TOOL-835T โปรดอ่านความคิดเห็นด้วย :)
for_each
อนุญาตให้เรานำรูปแบบ Fork-Joinมาใช้ อื่น ๆ กว่าที่จะสนับสนุนได้อย่างคล่องแคล่วอินเตอร์เฟซ
เราสามารถเพิ่มการนำgpu::for_each
ไปใช้เพื่อใช้ cuda / gpu สำหรับการคำนวณแบบต่างกันโดยการเรียกใช้งานแลมบ์ดาในพนักงานหลายคน
gpu::for_each(users.begin(),users.end(),update_summary);
// all summary is complete now
// go access the user-summary here.
และgpu::for_each
อาจรอให้คนงานทำงานกับแลมบ์ดา - ภารกิจให้เสร็จก่อนที่จะดำเนินการกับข้อความถัดไป
มันช่วยให้เราสามารถเขียนโค้ดที่มนุษย์อ่านได้อย่างกระชับ
accounts::erase(std::remove_if(accounts.begin(),accounts.end(),used_this_year));
std::for_each(accounts.begin(),accounts.end(),mark_dormant);
std::for_each
เมื่อใช้กับboost.lambda
หรือboost.bind
สามารถปรับปรุงความสามารถในการอ่านได้บ่อยครั้ง