22

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?

ecatmur
  • 152,476
  • 27
  • 293
  • 366

1 Answers1

20

It is legal in C++20.

[expr.static.cast]/4:

An expression E can be explicitly converted to a type T [...] if T is an aggregate type ([dcl.init.aggr]) having a first element x and there is an implicit conversion sequence from E to the type of x.

[dcl.init.aggr]/2:

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".

cpplearner
  • 13,776
  • 2
  • 47
  • 72