std::construct_at on top of existing object skipping re-initialization of some fields

In the following program, in a constant expression, a temporary object of A is created with all fields initialized, and then function f creates another object of A at the same address, skipping (re)initialization of the field x, which is read afterwards:

#include <memory>

struct A {
    int x;
    constexpr A() {}
    constexpr A(int xx) : x(xx) {}
};

constexpr int f(A && a) { 
    std::construct_at<A>(&a);
    return a.x; 
}

static_assert( f(A{5}) == 5 ); //ok in GCC only

GCC accepts it just fine. But other compilers complain, e.g. Clang:

note: read of uninitialized object is not allowed in a constant expression
    return a.x; 
           ^

Demo: https://gcc.godbolt.org/z/87zrEb7q7

Indeed x is not initialized in std::construct_at<A>(&a), but it was initialized in A{5}.

Which compiler is right here?


Solution 1:

I'm convinced this is UB, and GCC is wrong.

std::construct_at<A>(&a) creates a new A object, and thus a new int x member in it. That new object isn't initialized.

For this to be legal, there would have to be a special rule that uninitialized objects may get values based on the contents of memory they occupy, and I don't think such rule exists. [basic.indet] doesn't mention it.