3 ms·
Or don't do that (the char array trick) in C++ because implementers and standardizers are not clear about when/if you are allowed to store other objects in an a
by temac 5y ago
Or don't do that (the char array trick) in C++ because implementers and standardizers are not clear about when/if you are allowed to store other objects in an array of chars, and even if you are it is tricky because you need to manually manage the alignment, or they are attempting to replace char with std::byte in the long term but don't really have a comprehensive and detailed plan to do so, etc.
The implementation should probably provide you with std::aligned_storage which may involve some magic to handle some of those concerns, although merely the easiest ones, so I would say probably still don't use that either, unless you are already quite a C++ expert and/or are prepared to dig into the standard with no clear response about what you are attempting to do is even formally possible (the implementers/standardizers do not even know some things they make impossible for quite a long time, see for example the insanity of std::launder, or if you want to loose your mind forever the semantic of pointer provenance analysis that compilers are maybe already using to "optimize" but that what the semantic should even be is still being debated.)
- sillycross 5y agoYes, strict aliasing (or type-based alias analysis?) is quite crazy, and there are some murky dark corners where the specification is different between C and C++.. I think they have a std::launder thing exactly for this purpose of "safely casting an array of bytes into an object". However, in this particular case (of using char array to hide real implementation), the implementation resides in another translation unit, so I don't think anything is going to break if LTO is not enabled. With LTO I have no idea..
- temac 5y agoFor projects of mixed quality without basically an unbounded workforce maintaining them (who could investigate rare/arcane bugs "introduced" by the "optimizers" in some builds), and/or using "tricks", I too am fond of not using LTO. But then I force myself to find a second reason for why the program will run correctly, and unfortunately nowadays it is more and more being strictly-conforming. Relearning std::launder, TBAA, pointer provenance, etc. every time is way too consuming. I'm forced to give-up on programmer optimization and hope for the compiler to be really up to its mythical promises (and this yet: without LTO; too dangerous...)
- int_19h 5y agoYou don't need to cast anything - just placement-new it inside the array. So long as the array is properly aligned, this is fine. After that, it would be UB to peek at the bytes of the object via the array because of aliasing issues, but I don't see why it would be improper to use the pointer returned by new.
- sillycross 5y agoAfter the object is constructed by placement-new, the class methods still needs to reinterpret_cast the char array to an object pointer to access the object. I don't think in this specific case there is an UB involved, but I'm not language standard lawyer so I'm not sure. I feel the standard's specification on what is allowed to reinterpret_cast and what isn't is arcane (or at least far from straightforward to understand).
- tylerhou 5y agoNot too terrible to create a private method that does the reinterpret_cast for you. I've had to use this technique in the past and therefore dived into the standard for quite a while. I don't recall encountering any UB concerns.
- int_19h 5y agoYou will get the properly typed pointer to object from new, so if you want to play it completely safe wrt UB, it can be stashed away alongside the array in the public class; this adds sizeof(T*) to the latter, but avoids casts entirely. But, yes, you do technically need std::launder to get it directly via the array.
- tylerhou 5y ago> After that, it would be UB to peek at the bytes of the object via the array because of aliasing issues Do you have a source for this? IIRC char and std::byte have a specific aliasing exception. I.e. char* and std::byte are allowed to alias anything. You obviously aren't allowed to modify the char or std::byte array (because that would violate the struct/class's aliasing rule).
- bigcheesegs 5y agoThis is incorrect. I participated in the committee discussions around launder, byte, pointer provenance, and implicit object creation. None of those issues show up here. This is a very simple case of using placement new into a properly aligned char buffer to create an object at that location. This has worked just fine since C++98 and is not impacted by the many other object/lifetime/pointer issues that are being discussed. Additionally, implementers are in completely agreement here that this works. There are zero standardization/implementation concerns with this method, and I would highly advise against scaring users away from it when necessary.
- sillycross 5y agoThanks for clarification. It's nice to know that this approach is fully standard compliant. (and that's exactly my point: the standard is hard to access for normal devs like me, so I can guess "some reinterpret_cast hack seems ok", but never be 100% certain until some standard expert confirms)