Does the getValue() member function below violate the c++ strict aliasing rule?
According to the standard, I believe setValue() violates the strict aliasing rule since double is neither an aggregate type nor the base class of IEEE754_64.
What about getValue()? Is it an undefined behavior when the data member is in bit field form as the example below?
I am using similar code in a large project. GCC -O2 and -O3 output the wrong value. And the issue is gone if I add -fno-strict-aliasing. Also, the issue is gone if I use memcpy instead of casting in getValue(). Not sure if it is an GCC bug.
#include <iostream>
#include <cstring>
using namespace std;
struct IEEE754_64
{
void setValue(double);
unsigned long long getValue();
// Data members
unsigned long long d_mantissa : 52;
long long d_exponent : 11;
unsigned long long d_sign : 1;
};
void IEEE754_64::setValue(double d)
{
(*this) = *reinterpret_cast<IEEE754_64*>(&d);
}
unsigned long long IEEE754_64::getValue()
{
return * reinterpret_cast<unsigned long long *>(this);
}
int main()
{
double b = 1.0;
IEEE754_64 d;
memcpy(&d, &b, sizeof(double));
cout<<hex<<d.getValue()<<endl;
d.setValue(1.0);
cout<<hex<<d.getValue()<<endl;
return 0;
}
The behaviour of reinterpret_cast<T *>(&a)
depends on what object is actually at the memory location of a
.
Informally, if there is actually a T
object there too (which can, of course, only happen if the T
is a subobject of a
or vice versa) then the result of the cast is a pointer to the T
object.
Otherwise the result of the cast is a pointer to a
with the wrong type and reading through it may violate the strict aliasing rule.
Formally, the above is explained in the Standard under the sections [expr.static.cast]/13 , and [basic.compound]/4, see here for detail.
With that in mind, setValue
reads a double
through an lvalue of type IEEE754_64
, there is no doubt that this is a strict aliasing violation.
For the getValue
case we have to understand the behaviour of reinterpret_cast<unsigned long long *>(this)
which is less straight forward.
According to [basic.compound]/4, an object and its first non-static data member are always pointer-interconvertible. It does not list any exception for bitfields.
But the relevant text from [expr.static.cast]/13 is:
Otherwise, if the original pointer value points to an object
a
, and there is an objectb
of typeT
(ignoring cv-qualification) that is pointer-interconvertible witha
, the result is a pointer tob
.
If we accept that the bit-field is "an object of type unsigned long long
", it follows that the result of the cast is a pointer to a bit-field. However the Standard does not define the behaviour of pointers to bit-fields.
So, IMHO, the best way to interpret the above text is to say that the bit-field is not an object of type unsigned long long
. I believe this is consistent with the rest of the standard; even though there are no prvalues of bit-field type , it definitely does talk about glvalues of bit-field type.
Summing up; I believe the result of reinterpret_cast<unsigned long long *>(this)
is NOT a pointer to this->d_mantissa
, therefore the getValue()
function accesses an object of type IEEE754_64
using a glvalue of type unsigned long long
, violating the strict aliasing rule.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With