เราสามารถแก้ปัญหานี้โดยใช้คุณสมบัติ C ++ ได้หรือไม่?
C ++ 11 ได้เพิ่มฟังก์ชันสมาชิก ref-qualifiers ซึ่งอนุญาตให้ จำกัด ประเภทค่าของคลาสอินสแตนซ์ (นิพจน์) ที่ฟังก์ชันสมาชิกสามารถเรียกใช้ได้ ตัวอย่างเช่น:
struct foo {
void bar() & {} // lvalue-ref-qualified
};
foo& lvalue ();
foo prvalue();
lvalue ().bar(); // OK
prvalue().bar(); // error
เมื่อเรียกใช้begin
ฟังก์ชันสมาชิกเรารู้ว่าส่วนใหญ่เราจะต้องเรียกend
ฟังก์ชันสมาชิก (หรือบางอย่างเช่นsize
เพื่อให้ได้ขนาดของช่วง) สิ่งนี้ต้องการให้เราดำเนินการกับค่า lvalue เนื่องจากเราจำเป็นต้องจัดการสองครั้ง คุณสามารถยืนยันได้ว่าฟังก์ชั่นสมาชิกเหล่านี้ควรมีคุณสมบัติการรับรอง lvalue
อย่างไรก็ตามสิ่งนี้อาจไม่สามารถแก้ปัญหาพื้นฐานได้: aliasing begin
และend
ฟังก์ชั่นสมาชิกนามแฝงวัตถุหรือทรัพยากรในการจัดการโดยวัตถุ หากเราแทนที่begin
และend
ด้วยฟังก์ชั่นเดียวrange
เราควรจัดเตรียมหนึ่งฟังก์ชันที่สามารถเรียกใช้บนค่า rvalues:
struct foo {
vector<int> arr;
auto range() & // C++14 return type deduction for brevity
{ return std::make_pair(arr.begin(), arr.end()); }
};
for(auto const& e : foo().range()) // error
นี่อาจเป็นกรณีการใช้งานที่ถูกต้อง แต่คำนิยามข้างต้นrange
ไม่อนุญาต เนื่องจากเราไม่สามารถระบุที่อยู่ชั่วคราวหลังจากการเรียกฟังก์ชันสมาชิกอาจมีเหตุผลมากกว่าที่จะส่งคืนคอนเทนเนอร์เช่นช่วงการเป็นเจ้าของ:
struct foo {
vector<int> arr;
auto range() &
{ return std::make_pair(arr.begin(), arr.end()); }
auto range() &&
{ return std::move(arr); }
};
for(auto const& e : foo().range()) // OK
ใช้สิ่งนี้กับเคสของ OP และตรวจสอบโค้ดเล็กน้อย
struct B {
A m_a;
A & a() { return m_a; }
};
ฟังก์ชันนี้สมาชิกเปลี่ยนประเภทค่าของนิพจน์: B()
เป็น prvalue แต่B().a()
เป็น lvalue ในทางกลับกันB().m_a
ค่า rvalue ดังนั้นเริ่มต้นด้วยการทำให้สิ่งนี้สอดคล้องกัน มีสองวิธีในการทำสิ่งนี้:
struct B {
A m_a;
A & a() & { return m_a; }
A && a() && { return std::move(m_a); }
// or
A a() && { return std::move(m_a); }
};
รุ่นที่สองตามที่กล่าวข้างต้นจะแก้ไขปัญหาใน OP
นอกจากนี้เราสามารถ จำกัดB
ฟังก์ชั่นของสมาชิก:
struct A {
// [...]
int * begin() & { return &v[0]; }
int * end () & { return &v[3]; }
int v[3];
};
สิ่งนี้จะไม่มีผลกระทบใด ๆ กับรหัสของ OP เนื่องจากผลลัพธ์ของนิพจน์หลังจาก:
อยู่ในช่วงสำหรับลูปถูกผูกไว้กับตัวแปรอ้างอิง และตัวแปรนี้ (เป็นนิพจน์ที่ใช้ในการเข้าถึงbegin
และend
ฟังก์ชั่นสมาชิก) เป็นค่า lvalue
แน่นอนคำถามคือว่าหรือไม่กฎเริ่มต้นที่ควรจะเป็น"aliasing ฟังก์ชั่นสมาชิกใน rvalues ควรกลับวัตถุซึ่งเป็นเจ้าของทรัพยากรทั้งหมดของมันเว้นแต่มีเหตุผลที่ดีที่จะไม่" นามแฝงที่ส่งคืนสามารถใช้อย่างถูกต้องตามกฎหมาย แต่เป็นอันตรายในวิธีที่คุณประสบ: ไม่สามารถใช้เพื่อยืดอายุการใช้งานของ "ผู้ปกครอง" ชั่วคราว:
// using the OP's definition of `struct B`,
// or version 1, `A && a() &&;`
A&& a = B().a(); // bug: binds directly, dangling reference
A const& a = B().a(); // bug: same as above
A a = B().a(); // OK
A&& a = B().m_a; // OK: extends the lifetime of the temporary
ใน C ++ 2a ฉันคิดว่าคุณควรจะแก้ไขปัญหานี้ (หรือคล้ายกัน) ดังนี้:
for( B b = bar(); auto i : b.a() )
แทนของ OP
for( auto i : bar().a() )
วิธีแก้ปัญหาด้วยตนเองระบุว่าอายุการใช้งานของb
คือบล็อกทั้งหมดของ for-loop
ข้อเสนอที่แนะนำให้รู้จักกับแถลงการณ์นี้
การสาธิตสด