Search code examples
c++language-lawyerlifetimetemporaryreference-binding

C++ - using const reference to prolong a member of a temporary, ok or UB?


consider something like this:

#include <iostream>

struct C {
    C(double x=0, double y=0): x(x) , y(y) {
        std::cout << "C ctor " << x << " " <<y << " "  << "\n";
    }
    double x, y;
};

struct B {
    B(double x=0, double y=0): x(x), y(y) {}
    double x, y;
};

struct A {
    B b[12];

    A() {
        b[2] = B(2.5, 14);
        b[4] = B(56.32,11.99);
    }
};


int main() {
    const B& b = A().b[4];
    C c(b.x, b.y);
}

when I compile with -O0 I'm getting the print

C ctor 56.32 11.99

but when I compile with -O2 I'm getting

 C ctor 0 0

I know we can use const reference to prolong a local temporary, so something like

const A& a = A();
const B& b = a.b;

would be perfectly legal. but I'm struggling to find the reasoning for why the same mechanism/rule doesn't apply for any kind of temporary

EDIT FOR FUTURE REFERENCE:

I'm using gcc version 6.3.0


Solution

  • Your code should be well-formed, because for temporaries

    (emphasis mine)

    Whenever a reference is bound to a temporary or to a subobject thereof, the lifetime of the temporary is extended to match the lifetime of the reference

    Given A().b[4], b[4] is the subobject of b and the data member b is the subobject of the temproray A(), whose lifetime should be extended.

    LIVE on clang10 with -O2
    LIVE on gcc10 with -O2

    BTW: This seems to be a gcc's bug which has been fixed.

    From the standard, [class.temporary]/6

    The third context is when a reference is bound to a temporary object.36 The temporary object to which the reference is bound or the temporary object that is the complete object of a subobject to which the reference is bound persists for the lifetime of the reference if the glvalue to which the reference is bound was obtained through one of the following:

    ...

    [ Example:

    template<typename T> using id = T;
    
    int i = 1;
    int&& a = id<int[3]>{1, 2, 3}[i];          // temporary array has same lifetime as a
    const int& b = static_cast<const int&>(0); // temporary int has same lifetime as b
    int&& c = cond ? id<int[3]>{1, 2, 3}[i] : static_cast<int&&>(0);
                                               // exactly one of the two temporaries is lifetime-extended
    

    — end example ]