ต่อไปนี้เป็นตัวอย่าง (contrived) หรือไม่หรือเป็นพฤติกรรมที่ไม่ได้กำหนดไว้:
// undefined behavior?
const auto& c = SomeClass{};
// use c in code later
const auto& v = c.GetSomeVariable();
ต่อไปนี้เป็นตัวอย่าง (contrived) หรือไม่หรือเป็นพฤติกรรมที่ไม่ได้กำหนดไว้:
// undefined behavior?
const auto& c = SomeClass{};
// use c in code later
const auto& v = c.GetSomeVariable();
คำตอบ:
มันมีความปลอดภัย. Const ref ยืดอายุการใช้งานของชั่วคราว ขอบเขตจะเป็นขอบเขตของการอ้างอิง const
อายุการใช้งานของวัตถุชั่วคราวอาจขยายได้โดยผูกกับการอ้างอิง lvalue const หรือการอ้างอิง rvalue (ตั้งแต่ C ++ 11) ดู การเริ่มต้นการอ้างอิงสำหรับรายละเอียด
เมื่อใดก็ตามที่การอ้างอิงถูกผูกไว้กับชั่วคราวหรือไปยัง subobject ของมันอายุการใช้งานของชั่วคราวจะถูกขยายให้ตรงกับอายุการใช้งานของการอ้างอิงโดยมีข้อยกเว้นดังต่อไปนี้:
- ขอบเขตที่ถูกผูกไว้กับค่าส่งคืนของฟังก์ชันในคำสั่งส่งคืนจะไม่ถูกขยาย: มันจะถูกทำลายทันทีที่ส่วนท้ายของนิพจน์ส่งคืน ฟังก์ชันดังกล่าวส่งคืนการอ้างอิงที่ห้อยอยู่เสมอ
- ผูกชั่วคราวกับสมาชิกอ้างอิงในรายการเริ่มต้นคอนสตรัคยังคงอยู่เพียงจนกว่านวกรรมิกจะออกไม่ตราบเท่าที่วัตถุที่มีอยู่ (หมายเหตุ: การกำหนดค่าเริ่มต้นนั้นไม่ถูกต้อง ณ วันที่ DR 1696)
- มีข้อผูกมัดชั่วคราวกับพารามิเตอร์อ้างอิงในการเรียกใช้ฟังก์ชันจนกว่าจะสิ้นสุดการแสดงออกเต็มรูปแบบที่มีการเรียกใช้ฟังก์ชันนั้น: หากฟังก์ชันส่งคืนการอ้างอิงซึ่งอยู่เหนือการแสดงออกเต็มรูปแบบ
- ถูกผูกไว้ชั่วคราวกับการอ้างอิงใน initializer ที่ใช้ในการแสดงออกใหม่ที่มีอยู่จนถึงจุดสิ้นสุดของการแสดงออกเต็มรูปแบบที่มีการแสดงออกใหม่ที่ไม่ตราบเท่าที่วัตถุเริ่มต้น หากวัตถุที่ถูกเตรียมใช้งานอยู่เหนือกว่าการแสดงออกเต็มสมาชิกอ้างอิงของมันจะกลายเป็นข้อมูลอ้างอิงห้อย
- มีการผูกมัดชั่วคราวกับการอ้างอิงในองค์ประกอบการอ้างอิงของการรวมเริ่มต้นโดยใช้ไวยากรณ์เริ่มต้นโดยตรง (วงเล็บ) ซึ่งตรงข้ามกับไวยากรณ์รายการเริ่มต้น (วงเล็บปีกกา) มีอยู่จนถึงจุดสิ้นสุดของการแสดงออกเต็มรูปแบบที่มี initializer
struct A { int&& r; }; A a1{7}; // OK, lifetime is extended A a2(7); // well-formed, but dangling reference
โดยทั่วไปอายุการใช้งานของชั่วคราวไม่สามารถขยายได้อีกต่อไปโดย "ส่งต่อ": การอ้างอิงที่สองซึ่งเริ่มต้นจากการอ้างอิงที่การผูกชั่วคราวไม่ส่งผลต่ออายุการใช้งาน
ตามที่@ Konrad Rudolphชี้ให้เห็น (และดูย่อหน้าสุดท้ายของด้านบน):
"ถ้า
c.GetSomeVariable()
ส่งคืนการอ้างอิงไปยังวัตถุภายในเครื่องหรือการอ้างอิงว่าตัวมันเองยืดอายุการใช้งานของวัตถุบางอย่างการขยายอายุการใช้งานจะไม่ส่งผลต่อ"
c.GetSomeVariable()
ส่งคืนการอ้างอิงไปยังวัตถุในท้องถิ่นหรือการอ้างอิงว่าตัวมันเองยืดอายุการใช้งานของวัตถุการขยายอายุการใช้งานจะไม่เริ่มขึ้น
ใช่มันปลอดภัยอย่างสมบูรณ์: การผูกพันกับ const
อ้างอิงจะขยายอายุการใช้งานของชั่วคราวไปจนถึงขอบเขตของการอ้างอิงนั้น
หมายเหตุว่าพฤติกรรมที่ไม่ได้เป็นสกรรมกริยาแม้ว่า ตัวอย่างเช่นด้วย
const auto& cc = []{
const auto& c = SomeClass{};
return c;
}();
cc
dangles
นี่คือความปลอดภัย
[class.temporary]/5
: มีสามบริบทที่ชั่วคราวจะถูกทำลายที่จุดที่แตกต่างกันจากปลายที่มีเต็มรูปแบบในการแสดงออก [ .. ]
[class.temporary]/6
: บริบทที่สามคือเมื่อการอ้างอิงถูกผูกไว้กับวัตถุชั่วคราว วัตถุชั่วคราวที่การอ้างอิงถูกผูกไว้หรือวัตถุชั่วคราวที่เป็นวัตถุที่สมบูรณ์ของ subobject ที่การอ้างอิงถูกผูกไว้ยังคงอยู่ตลอดชีวิตของการอ้างอิงถ้า glvalue ที่การอ้างอิงถูกผูกไว้ผ่านหนึ่งในต่อไปนี้ : [สิ่งต่างๆมากมายที่นี่]
ปลอดภัยในกรณีเฉพาะนี้ อย่างไรก็ตามโปรดทราบว่าไม่สามารถใช้ชั่วคราวได้ทั้งหมดโดยการอ้างอิง const ... ตัวอย่างเช่น
#include <stdio.h>
struct Foo {
int member;
Foo() : member(0) {
printf("Constructor\n");
}
~Foo() {
printf("Destructor\n");
}
const Foo& method() const {
return *this;
}
};
int main() {
{
const Foo& x = Foo{}; // safe
printf("here!\n");
}
{
const int& y = Foo{}.member; // safe too (special rule for this)
printf("here (2)!\n");
}
{
const Foo& z = Foo{}.method(); // NOT safe
printf("here (3)!\n");
}
return 0;
}
การอ้างอิงที่ได้รับสำหรับz
ไม่ปลอดภัยที่จะใช้เพราะอินสแตนซ์ชั่วคราวจะถูกทำลายเมื่อสิ้นสุดการแสดงออกเต็มก่อนที่จะถึงprintf
คำสั่ง ผลลัพธ์คือ:
Constructor
here!
Destructor
Constructor
here (2)!
Destructor
Constructor
Destructor
here (3)!