ข้อดีอย่างหนึ่งของstd::beginและstd::endคือพวกเขาทำหน้าที่เป็นจุดส่วนขยายสำหรับการใช้อินเตอร์เฟซมาตรฐานสำหรับชั้นเรียนภายนอก
หากคุณต้องการใช้CustomContainerคลาสที่มีช่วงแบบอิงฟังก์ชันลูปหรือเทมเพลตที่คาดหวัง.begin()และ.end()วิธีการคุณต้องใช้วิธีการเหล่านั้นอย่างชัดเจน
หากชั้นเรียนมีวิธีการเหล่านั้นแสดงว่าไม่มีปัญหา หากไม่เป็นเช่นนั้นคุณจะต้องแก้ไข *
สิ่งนี้ไม่เป็นไปได้เสมอเช่นเมื่อใช้ห้องสมุดภายนอกแหล่งข้อมูลเชิงพาณิชย์และแหล่งปิด
ในสถานการณ์เช่นนี้std::beginและstd::endมีประโยชน์เนื่องจากเราสามารถให้ iterator API โดยไม่ต้องแก้ไขคลาสเอง แต่ให้โหลดฟรีมากเกินไป
ตัวอย่าง:สมมติว่าคุณต้องการใช้count_ifฟังก์ชั่นที่ใช้คอนเทนเนอร์แทนตัววนซ้ำคู่หนึ่ง รหัสดังกล่าวอาจมีลักษณะเช่นนี้:
template<typename ContainerType, typename PredicateType>
std::size_t count_if(const ContainerType& container, PredicateType&& predicate)
{
using std::begin;
using std::end;
return std::count_if(begin(container), end(container),
std::forward<PredicateType&&>(predicate));
}
ตอนนี้สำหรับคลาสใด ๆ ที่คุณต้องการใช้กับกำหนดเองนี้count_ifคุณเพียงแค่เพิ่มฟังก์ชั่นฟรีสองฟังก์ชันแทนการแก้ไขคลาสเหล่านั้น
ตอนนี้ C ++ มีกลไกที่เรียกว่าArgument Dependent Lookup
(ADL) ซึ่งทำให้วิธีการดังกล่าวมีความยืดหยุ่นมากยิ่งขึ้น
กล่าวโดยย่อคือ ADL หมายถึงว่าเมื่อคอมไพเลอร์แก้ไขฟังก์ชันที่ไม่มีเงื่อนไข (เช่นฟังก์ชันที่ไม่มีเนมสเปซเหมือนbeginแทนstd::begin) จะพิจารณาฟังก์ชันที่ประกาศในเนมสเปซของอาร์กิวเมนต์ ตัวอย่างเช่น:
namesapce some_lib
{
// let's assume that CustomContainer stores elements sequentially,
// and has data() and size() methods, but not begin() and end() methods:
class CustomContainer
{
...
};
}
namespace some_lib
{
const Element* begin(const CustomContainer& c)
{
return c.data();
}
const Element* end(const CustomContainer& c)
{
return c.data() + c.size();
}
}
// somewhere else:
CustomContainer c;
std::size_t n = count_if(c, somePredicate);
ในกรณีนี้มันไม่สำคัญว่าชื่อที่ผ่านการรับรองจะเป็นsome_lib::beginและsome_lib::end
- เนื่องจากCustomContainerมีอยู่some_lib::ด้วยคอมไพเลอร์จะใช้โอเวอร์โหลดเหล่านั้นcount_ifผู้ที่อยู่ใน
นั่นเป็นเหตุผลสำหรับการมีusing std::begin;และในusing std::end; count_ifสิ่งนี้ช่วยให้เราสามารถใช้งานได้อย่างไม่มีเงื่อนไขbeginและendอนุญาตให้ ADL และ
อนุญาตให้คอมไพเลอร์เลือกstd::beginและstd::endเมื่อไม่พบทางเลือกอื่น
เราสามารถกินคุกกี้และมีคุกกี้ - เช่นมีวิธีที่จะให้การใช้งานที่กำหนดเองของbegin/ endในขณะที่คอมไพเลอร์สามารถถอยกลับไปที่มาตรฐาน
หมายเหตุบางส่วน:
ด้วยเหตุผลเดียวกันนี้ยังมีฟังก์ชั่นอื่น ๆ ที่คล้ายกัน: std::rbegin/ rend,
และstd::sizestd::data
ในฐานะที่เป็นคำตอบอื่น ๆ กล่าวถึงstd::รุ่นที่มีมากเกินไปสำหรับอาร์เรย์เปล่า มีประโยชน์ แต่เป็นเพียงกรณีพิเศษของสิ่งที่ฉันได้อธิบายไว้ข้างต้น
การใช้std::beginและเพื่อน ๆ เป็นความคิดที่ดีโดยเฉพาะเมื่อเขียนโค้ดเทมเพลตเพราะสิ่งนี้ทำให้เทมเพลตเหล่านั้นมีความเป็นสากลมากขึ้น สำหรับแม่แบบที่ไม่ใช่คุณอาจใช้วิธีการเช่นกันเมื่อมี
ป.ล. ฉันทราบว่าโพสต์นี้เกือบ 7 ปีแล้ว ฉันเจอเพราะฉันต้องการตอบคำถามที่ถูกทำเครื่องหมายว่าซ้ำซ้อนและค้นพบว่าไม่มีคำตอบที่นี่ที่ระบุถึง ADL