6 ms·
Yes, 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+
by sillycross 5y ago
Yes, 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).
- int_19h 5y agoI'm not so sure anymore, after looking at the standard again. It says that it's okay to access any object via a glvalue of type char, unsigned char, or std::byte; note that this does not include arrays of the same. If you have a field of array type, and you subscript it, the expression does involve a glvalue of that array type as one of the operands - but does this constitute "access"?
- tylerhou 5y agoA little late, but yes, subscripting an array yields a gvalue of that type, but that is not an access unless you use the glvalue in some other computation (I assume). But even if you do access it (i.e. make a bitwise copy of the entire array) that should still not be UB.