Search code examples
c++gcclanguage-lawyerc++17object-lifetime

c++17: A temporary object never destroyed


struct Base {
    Base() {
        std::cout << "Inside:  " << __PRETTY_FUNCTION__ << std::endl;
    }
    ~Base() {
        std::cout << "Inside:  " << __PRETTY_FUNCTION__ << std::endl;
    }
};

struct BaseWrapper {
    const Base &b;
};

int main()
{
    {
        auto *w = new BaseWrapper{{}};
        std::cout << "Inside:  " << __PRETTY_FUNCTION__ << std::endl;
        delete w;
    }
    return 0;
}

The above code works as I expected when I compile it with C++11 or C++14, but when I compile it with C++17, it gives me something like this:

Inside:  Base::Base()                                                                                                                                                              
Inside:  int main()

As you can see, Base::~Base() was never called. Which doesn't make much sense to me. I have tested this code with GCC 7.3.0 on Ubuntu 18.04.1 LTS, and also with OnlineGDB. They all give the same result.

I'm just wondering if this is a new feature in C++17 or it is a bug?

Update: I'm well aware that w->b is a dangling reference. Actually, I deliberately wrote this piece of code just to show that. Then, I found this issue while testing it.

What I really want to know is how severe this issue is, in case I have to stick with GCC 7.3? or if there is any other ways to reproduce the same issue? or the defect report?


Solution

  • Clearly, a temporary that is created but not destroyed is a compiler bug - unless the standard says the behaviour is undefined. The example is well defined however. The relevant rules from the standard [class.temporary]:

    When an implementation introduces a temporary object of a class that has a non-trivial constructor ([class.default.ctor], [class.copy.ctor]), it shall ensure that a constructor is called for the temporary object. Similarly, the destructor shall be called for a temporary with a non-trivial destructor ([class.dtor]). Temporary objects are destroyed as the last step in evaluating the full-expression ([intro.execution]) that (lexically) contains the point where they were created. ...

    There are three contexts in which temporaries are destroyed at a different point than the end of the full-expression. ...

    The third context is when a reference is bound to a temporary object ...

    The exceptions to this lifetime rule are:

    • ...

    • A temporary bound to a reference in a new-initializer ([expr.new]) persists until the completion of the full-expression containing the new-initializer.