To my surprise, gcc 11.2 accepts this code, but only in C++20 mode:
struct Base {};
struct Derived : Base { int i; };
int f(Base& b) { return static_cast<Derived>(b).i; }
// ^~~~~~~ oops, forgot the `&`
Likewise, MSVC 19.29 accepts it in C++20 mode and rejects it in C++17 mode, but clang rejects it even in C++20 mode.
Looking at the assembler output, f
always returns 0
, so it's ignoring the actual data of any potential Derived
passed to f
.
Is this UB, or is it legal in C++20, and if so, why?
It is legal in C++20.
An expression
E
can be explicitly converted to a typeT
[...] ifT
is an aggregate type ([dcl.init.aggr]) having a first elementx
and there is an implicit conversion sequence fromE
to the type ofx
.
The elements of an aggregate are: [...] for a class, the direct base classes in declaration order, followed by the direct non-static data members ([class.mem]) that are not members of an anonymous union, in declaration order.
So Derived
is an aggregate whose first element is Base
, and since C++20, it is permitted to create an aggregate from its first element.
This feature is introduced in P1975R0 "Fixing the wording of parenthesized aggregate-initialization".