Does this really break strict-aliasing rules?
The warning is absolutely justified. The decayed pointer to data
does not point to an object of type int
, and casting it doesn't change that. See [basic.life]/7:
If, after the lifetime of an object has ended and before the storage
which the object occupied is reused or released, a new object is
created at the storage location which the original object occupied, a
pointer that pointed to the original object, a reference that referred
to the original object, or the name of the original object will
automatically refer to the new object and, once the lifetime of the
new object has started, can be used to manipulate the new object, if:
(7.1) — [..]
(7.2) — the new object is of the same type as the
original object (ignoring the top-level cv-qualifiers),
The new object is not an array of char
, but an int
. P0137, which formalizes the notion of pointing, adds launder
:
[ Note: If these conditions are not met, a pointer to the new object
can be obtained from a pointer that represents the address of its
storage by callingstd::launder
(18.6 [support.dynamic]). — end note
]
I.e. your snippet can be corrected thusly:
std::cout << *std::launder(reinterpret_cast<int*>(data));
.. or just initialize a new pointer from the result of placement new, which also removes the warning.
Does this break the strict aliasing rule?
Quoting C11
, chapter §6.5p7
An object shall have its stored value accessed only by an lvalue expression that has one of the following types:
- a type compatible with the effective type of the object
Also
Quoting C11
, chapter §6.5p6
The effective type of an object for an access to its stored value is the declared type of the object, if any....
Emphases mine
Here the effective type of the object pointed by num
is indeed int
and hence it is okay to dereference it with a pointer to int
.
Strict aliasing rules broken with templates and inheritance
Nope, there is no strict aliasing rule violation in the provided code. It looks like a bug in gcc.
You can submit a bugreport to gcc (I was not able to find anything already there related to the snippet provided), however, judging by life and times of https://gcc.gnu.org/bugzilla/show_bug.cgi?id=41874 I would not expect immediate fix.
Is this code breaking strict-aliasing rules?
You are attacking the strict aliasing rules from the wrong angle. Casting an array of char
to your structure would in fact be UB. This is not only because of aliasing but also because alignment properties can be different. Don't do that.
You'd have to do it the other way round: declare your structure of the real type that you want to have, and then use a void*
or char*
pointer to that structure to read or copy your data into it.
This is always guaranteed to work:
- character types are exempted from the strict aliasing rules
- passing a pointer of an object to a function (
memcpy
or other) always ensures that the compiler can't make any assumptions about the state of that object after the call, so he has to reload the entire object.
Edit: Perhaps some the confusion comes from the strange gcc warning about "aliasing rules". This is only one facet of the problems that come from type-punning through pointer casts. Generally accessing an object through a pointer of a wrong type other than character types can have undefined behavior. Aliasing is only one of several things that can go wrong with that. Just avoid it.
Does this code violate the strict aliasing rule?
No it doesn't, but this is only because the memory was allocated, and written into using a character type.
Memory is allocated using malloc. That object doesn't have declared1 type because it was allocated with malloc. Thus the object doesn't have any effective type.
Then the code accesses and modifies the object using the type char
. As the type is2 char
and no object having an effective type is copied5, copying doesn't set the effective type to char
for this and subsequent accesses, but sets the effective type to char
, only for the duration of the access3. After the access, the object doesn't have an effective type anymore.
Then the type int
is used to access and only read that object. As the object doesn't have an effective type, it becomes3 int
, for the duration of the read. After the access the object doesn't have an effective type anymore. As int
was obviously compatible with the effective type int
, the behavior is defined.
(Assuming the values read are not trap representation for int
.)
Had you accessed and modified the object using a non-character type that is also not compatible with int
, the behavior would be undefined.
Let's say your example was (assuming sizeof(float)==sizeof(int)
):
int i;
void *buf = calloc(5, sizeof(float)); // buf initialized to 0
{
float *ptr1 = buf;
for(i = 0; i < 5*sizeof(float); ++i)
ptr1[i] = (float)i;
}
int *ptr2 = buf;
for(i = 0; i < 5; ++i)
printf("%d", ptr2[i]);
The effective type of the object, when float
s are being written into, becomes of type float
, for the duration of the write and all subsequent accesses to the object that don't modify it2. When those objects are then accessed by int
the effective type remains float
, as the values are only being read not modified. The previous write using float
set the effective type to float
permanently until the next write into this object (which didn't happen in this case). Types int
and float
are not compatible4, thus the behavior is undefined.
(All text below is quoted from: ISO:IEC 9899:201x)
1 (6.5 Expressions 6)
The effective type of an object for an access to its stored value is the declared type of the object, if any. 87) Allocated objects have no declared type.
2 (6.5 Expressions 6)
If a value is stored into an object having no declared type through an lvalue having a type that is not a character type, then the type of the lvalue becomes the effective type of the object for that access and for subsequent accesses that do not modify the stored value.
3 (6.5 Expressions 6)
For all other accesses to an object having no declared type, the effective type of the object is simply the type of the lvalue used for the access.
4 (6.5 Expressions 8)
An object shall have its stored value accessed only by an lvalue expression that has one of
the following types: 88)
— a type compatible with the effective type of the object,
— a qualified version of a type compatible with the effective type of the object,
— a type that is the signed or unsigned type corresponding to the effective type of the
object,
— a type that is the signed or unsigned type corresponding to a qualified version of the
effective type of the object,
— an aggregate or union type that includes one of the aforementioned types among its
members (including, recursively, a member of a subaggregate or contained union), or
— a character type.
5 (6.5 Expressions 6)
If a value is copied into an object having no declared type using memcpy or memmove, or is copied as an array of character type, then the effective type of the modified object for that access and for subsequent accesses that do not modify the value is the effective type of the object from which the value is copied, if it has one.
Am I breaking strict aliasing rules?
There is only one intrinsic that "extracts" the lower order double value from xmm register:
double _mm_cvtsd_f64 (__m128d a)
You could use it this way:
return _mm_cvtsd_f64(x);
There is some contradiction between different references. MSDN says: This intrinsic does not map to any specific machine instruction
. While Intel intrinsic guide mentions movsd
instruction. In latter case this additional instruction is easily eliminated by optimizer. At least gcc 4.8.1 with -O2
flag generates code with no additional instruction.
Is it a violation of strict aliasing to cast to a super-class and back in C?
My primary question is whether do_with_a() is always defined behavior
As a sub-question: I believe that converting an struct a * to a struct header * by &ap->hdr or (struct header *)ap would both be well-defined, is this the case?
It's well-defined as per the initial member rule C17 6.7.2.1/15 emphasis mine:
Within a structure object, the non-bit-field members and the units in which bit-fields
reside have addresses that increase in the order in which they are declared. A pointer to a
structure object, suitably converted, points to its initial member (or if that member is a
bit-field, then to the unit in which it resides), and vice versa. There may be unnamed
padding within a structure object, but not at its beginning.This is also consistent with the effective type/strict aliasing rule you quote in 6.5 §6 and §7
I'm reasonably certain that do_with_b() will always be undefined behavior
Yes, it's not a compatible struct. So it's a strict aliasing violation and possibly also an alignment problem. Note however that the strict aliasing rule is compatible with an oddball rule called "common initial sequence", which in this case would allow you to inspect the header part of
b
. C17 6.5.2.3/6:One special guarantee is made in order to simplify the use of unions: if a union contains
several structures that share a common initial sequence (see below), and if the union
object currently contains one of these structures, it is permitted to inspect the common
initial part of any of them anywhere that a declaration of the completed type of the union
is visible. Two structures share a common initial sequence if corresponding members
have compatible types (and, for bit-fields, the same widths) for a sequence of one or more
initial members.That is, if you add something like
typedef union { struct a a_; struct b b_; } dummy;
to the translation unit, you would be allowed to inspect the header part of each struct in a well-defined manner. But not that compilers might have shaky standard compliance when it comes to implementing this feature and there were some defect reports about it to the committee (I'm unsure of its status as per C23).GCC, however, does signal warnings about both down-casts when set to -Wstrict-aliasing=1
These options in gcc have status somewhere between broken and very broken.
-fno_strict_aliasing
to disable it entirely is reliable however.The strict aliasing rule itself got many flaws: for example the effective type of the memory you allocated is actually a
struct header
and afloat
, not astruct a
, because you didn't write an lvalue of typestruct a
to the memory returned bymalloc
. Similarly, given that we allocate a chunk of memory withmalloc
then treat it as an array oftype
by initializing it in a for loop, then we actually don't have the effective typetype[]
but individual objects. But if implemented like that the whole C language unravels.
custom optional breaks strict aliasing rules
What can I do to make this class without UB?
- Use placement-new to create the object.
- Call the destructor of the created object in destructor of
optional
- Do not reinterpret the address of the storage. Use the pointer returned from placement new. This unfortunately means that you need to store the pointer as a member. You can replace the bool since null pointer would signify empty state.
- Take care of alignment. This can be quite tricky pre-C++11. You may need to rely on non-standard language features to achieve this. Given a pointer member which has quite strict alignment, and the fact that C++98 has no overaligned types, you might get away with ignoring alignment for most types.
It would be much easier to allocate the object dynamically. Slower, very likely. But simpler and standard conformant.
Related Topics
How to Use C++ with Objective-C in Xcode
Virtual Destructor and Undefined Behavior
How to Build Cmake Externalproject While Configurating Main One
Cmake Linking Against Shared Library on Windows: Error About Not Finding .Lib File
Ncurses and Qt Interoperability
Code Runs Perfect in G++ But Not in Xcode - Cannot Find File
Are Pointer Variables Just Integers with Some Operators or Are They "Symbolic"
Rounding Up and Down a Number C++
Using C++ to Edit the Registry
How to Use the Non-Default Constructor for a Member
How to Truncate a Floating Point Number After a Certain Number of Decimal Places (No Rounding)
Why Doesn't Left Bit Shift << Shift Beyond 31 for Long Int Datatype
Hash an Arbitrary Precision Value (Boost::Multiprecision::Cpp_Int)
Qml and C++ Image Interoperability
Is There a Use for Function Declarations Inside Functions
Why Lifetime of Temporary Doesn't Extend Till Lifetime of Enclosing Object