ข้อดีอย่างหนึ่งของ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::size
std::data
ในฐานะที่เป็นคำตอบอื่น ๆ กล่าวถึงstd::
รุ่นที่มีมากเกินไปสำหรับอาร์เรย์เปล่า มีประโยชน์ แต่เป็นเพียงกรณีพิเศษของสิ่งที่ฉันได้อธิบายไว้ข้างต้น
การใช้std::begin
และเพื่อน ๆ เป็นความคิดที่ดีโดยเฉพาะเมื่อเขียนโค้ดเทมเพลตเพราะสิ่งนี้ทำให้เทมเพลตเหล่านั้นมีความเป็นสากลมากขึ้น สำหรับแม่แบบที่ไม่ใช่คุณอาจใช้วิธีการเช่นกันเมื่อมี
ป.ล. ฉันทราบว่าโพสต์นี้เกือบ 7 ปีแล้ว ฉันเจอเพราะฉันต้องการตอบคำถามที่ถูกทำเครื่องหมายว่าซ้ำซ้อนและค้นพบว่าไม่มีคำตอบที่นี่ที่ระบุถึง ADL