3

Is the following code well defined?

struct S { int x; };
alignas(alignof(S)) char c_arr[sizeof(S)];
S *s_ptr = (S*)c_arr;
s_ptr->x = 5; // UB or not UB?

Note: S is purposfully defined as a Trivial Type.

Would the situation change if we made the type none trivial by adding a constructor that just sets X to some arbitrary value without calling it through placement new?

This question is different from this question because it does not use malloc and does ask also about none trivial types.

Brotcrunsher
  • 1,964
  • 10
  • 32
  • Yes, this is UB. IIRC the `malloc` (without placement new) is no longer UB since C++20. – HolyBlackCat Aug 13 '21 at 20:46
  • @HolyBlackCat Technically, also no longer in C++17 since the change was accepted as a defect resolution while C++17 was the official standard. – eerorika Aug 13 '21 at 20:51
  • @eerorika A defect, huh. I thought they applied retroactively to every standard version. – HolyBlackCat Aug 13 '21 at 20:53
  • @HolyBlackCat which change would that be? I think I missed it. – SergeyA Aug 13 '21 at 20:54
  • Looks like text book violation of strict aliasing rule to me. – SergeyA Aug 13 '21 at 20:54
  • @SergeyA I don't have the exact proposal/DR, but I have this: http://eel.is/c++draft/c.malloc#4 – HolyBlackCat Aug 13 '21 at 20:56
  • @HolyBlackCat I can't understand what it means. Which objects are created by malloc? May be you have a link to any explanation at all? – SergeyA Aug 13 '21 at 20:57
  • 1
    @HolyBlackCat I remember reading that defect resolutions apply to the current standard and optionally any of the previous ones at the discretion of the implementer. Given that all compilers worked with nominally UB malloc + assign as far as I know, I don't see a reason why not support it. I don't know if that is actually specified anywhere. – eerorika Aug 13 '21 at 20:58
  • @SergeyA Doesn't the strict alias rule only apply when I change the same memory through two different kinds of representations? Note that I never change anything through c_arr. Also I thought char is excluded from the strict aliasing rule? – Brotcrunsher Aug 13 '21 at 21:00
  • @Brotcrunsher chars exclusion is one way - you can access something as char through pointer which is not a `char*`. It doesn't work the other way around. Since there is no S object pointed to by `s_ptr` (it points to char array) the strict aliasing rule defines this code as UB – SergeyA Aug 13 '21 at 21:07
  • @SergeyA Nope, I didn't explore this topic much yet. – HolyBlackCat Aug 13 '21 at 21:26

1 Answers1

3

Is the following code well defined?

No. It violates the so called strict aliasing rule.

You must launder the pointer:

S *s_ptr = std::launder(reinterpret_cast<S*>(c_arr));
s_ptr->x = 5; // not UB

Prior to acceptance of proposal P0593R6, laundering would not have been sufficient. Instead, it used to be necessary to explicitly create the object using placement-new. That is still necessary when the reinterepreted type is not an "implicit-lifetime-type".

eerorika
  • 232,697
  • 12
  • 197
  • 326
  • Seems P0593R4 wasn't merged. It mentions `std::bless`, which we don't have. – HolyBlackCat Aug 13 '21 at 21:13
  • 1
    @HolyBlackCat Yeah, that was an old version. I've updated the link. – eerorika Aug 13 '21 at 21:14
  • Hmm. Can `launder` start object lifetime? http://eel.is/c++draft/ptr.launder#lib:launder doesn't say anything about it. – HolyBlackCat Aug 13 '21 at 21:20
  • 1
    @HolyBlackCat `launder` can't. But the array of chars can. Standard says: `An operation that begins the lifetime of an array of char, unsigned char, or std::byte implicitly creates objects within the region of storage occupied by the array.` – eerorika Aug 13 '21 at 21:23
  • 1
    [`[intro.object]/10`](http://eel.is/c++draft/basic#intro.object-10) *"starts the lifetime of ... objects ... if doing so would result in the program having defined behavior. If no such ... objects would give the program defined behavior, the behavior of the program is undefined."* This is just brilliant. – HolyBlackCat Aug 13 '21 at 21:44