Why is this value downcast (static_cast to object type) allowed in C++20?
Asked Answered
W

1

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?

Winepress answered 10/8, 2021 at 7:32 Comment(0)
C
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".

Coverage answered 10/8, 2021 at 7:46 Comment(4)
This is one of the most counter-intuitive pieces of code I've seen in C++20. Aggregates continue being an unpredictable mess.Universal
So I can write int arr[2] = {1,2} ; cout << (int)arr; and it will print 1?Galenic
@Peter-ReinstateMonica That would print a pointer. It's the other way around, static_cast<int[2]>(42) is now legal it seems.Hertzfeld
@Hertzfeld Oh. I see, yes. Basically, you can convert an element to an aggregate of elements. In a way, they always were arrays of length 1, conceptually.Galenic

© 2022 - 2024 — McMap. All rights reserved.