กำลังจับวัตถุที่สร้างขึ้นใหม่โดย const อ้างอิงพฤติกรรมที่ไม่ได้กำหนด


11

ต่อไปนี้เป็นตัวอย่าง (contrived) หรือไม่หรือเป็นพฤติกรรมที่ไม่ได้กำหนดไว้:

// undefined behavior?
const auto& c = SomeClass{};

// use c in code later
const auto& v = c.GetSomeVariable();

คำตอบ:


12

มันมีความปลอดภัย. 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()ส่งคืนการอ้างอิงไปยังวัตถุภายในเครื่องหรือการอ้างอิงว่าตัวมันเองยืดอายุการใช้งานของวัตถุบางอย่างการขยายอายุการใช้งานจะไม่ส่งผลต่อ"


1
คุณควรอ้างอิงแหล่งที่มาของคำพูดนั้น
Lightness Races ใน Orbit

@LightnessRaceswithMonica เสร็จแล้ว ฉันกำลังมองหาข้อความที่ดีกว่า
ให้อภัย

2
มันเป็นการดีที่จะขีดเส้นใต้ว่านี่เป็นความจริงสำหรับค่าเท่านั้น หากc.GetSomeVariable()ส่งคืนการอ้างอิงไปยังวัตถุในท้องถิ่นหรือการอ้างอิงว่าตัวมันเองยืดอายุการใช้งานของวัตถุการขยายอายุการใช้งานจะไม่เริ่มขึ้น
Konrad Rudolph

@ KonradRudolph ขอบคุณ! ฉันเพิ่มข้อยกเว้นด้วย
ให้อภัย

4

ควรจะมีปัญหาไม่มีที่นี่ขอบคุณขยายอายุการใช้งาน วัตถุที่สร้างขึ้นใหม่จะอยู่รอดได้จนกว่าการอ้างอิงจะออกนอกขอบเขต


3

ใช่มันปลอดภัยอย่างสมบูรณ์: การผูกพันกับ constอ้างอิงจะขยายอายุการใช้งานของชั่วคราวไปจนถึงขอบเขตของการอ้างอิงนั้น

หมายเหตุว่าพฤติกรรมที่ไม่ได้เป็นสกรรมกริยาแม้ว่า ตัวอย่างเช่นด้วย

const auto& cc = []{
    const auto& c = SomeClass{};
    return c;
}();

cc dangles


2

นี่คือความปลอดภัย

[class.temporary]/5: มีสามบริบทที่ชั่วคราวจะถูกทำลายที่จุดที่แตกต่างกันจากปลายที่มีเต็มรูปแบบในการแสดงออก [ .. ]

[class.temporary]/6: บริบทที่สามคือเมื่อการอ้างอิงถูกผูกไว้กับวัตถุชั่วคราว วัตถุชั่วคราวที่การอ้างอิงถูกผูกไว้หรือวัตถุชั่วคราวที่เป็นวัตถุที่สมบูรณ์ของ subobject ที่การอ้างอิงถูกผูกไว้ยังคงอยู่ตลอดชีวิตของการอ้างอิงถ้า glvalue ที่การอ้างอิงถูกผูกไว้ผ่านหนึ่งในต่อไปนี้ : [สิ่งต่างๆมากมายที่นี่]


1

ปลอดภัยในกรณีเฉพาะนี้ อย่างไรก็ตามโปรดทราบว่าไม่สามารถใช้ชั่วคราวได้ทั้งหมดโดยการอ้างอิง 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)!
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.