"Move or throw" using the ternary operator

Viewed 179

Since C++11 I have been using the ternary operator to move or throw based on some condition, but with latest GCC (9.1 and trunk) is not working anymore.

I have reduced the problem to this example (Wandbox permalink):

#include <iostream>
#include <memory>

int main() 
{
    auto p = std::make_unique<int>();
    std::cout << "p.get(): " << p.get() << std::endl;

    {
        std::cout << "Move p into q" << std::endl;
        auto q = p ? std::move(p) : throw std::invalid_argument{"null ptr"};

        std::cout << "q.get(): " << q.get() << std::endl;
    }

    std::cout << "p.get(): " << p.get() << std::endl;
    return 0;
}

It works with GCC 8.3 and older, and with every Clang version; and p is moved:

p.get(): 0xde5c20
Move p into q
q.get(): 0xde5c20
p.get(): 0

But now with GCC 9.1 and later it doesn't work:

p.get(): 0x1d89150
Move p into q
q.get(): 0x1d89150
p.get(): 0x1d89150

And then the program crashes due to a double free.

Is this a bug in GCC 9.1/trunk? Or latest GCC is the only one doing the right thing and this is not valid C++?

1 Answers

This has to be a bug.

Either the move will be a move or the compilation should fail due to attempted copy of a unique_ptr (or a throw occurs in which case it doesn't matter).

q.get() == p.get() indeed shows that the internals have broken down, as this should not be possible.

I'd agree with cpplearner that this is bug 90393 (and all its dupes), which is reported as having been introduced in GCC 9.1.

Either downgrade, wait to upgrade, or reform your code into a nice if/else.

Related