C++ reinterpret_cast - will this always work corre

2019-08-10 20:33发布

问题:

I have written MyString and MyStringConst class. Now I need from time to time pass MyString as MyStringConst, hence overload cast operator. I have written this

MyString::operator const MyStringConst &() const
{
    return reinterpret_cast<const MyStringConst &>(*this);
}

MyString has this data

char * str;
int length;
volatile int hashCode;
int bufferSize;

MyStringConst has this data

const char * c_str;
int length;
volatile int hashCode;

Plus there are some methods, that in both strings can recalculate hashCode.

Is this code correctly written. I have tested it on MSVC 2013 and it is working correctly, but I have no idea if it can be used in production code, that can be compiled with different compiler.

回答1:

The common initial sequence of the data member is different and C++ makes no guarantee at all about the layout in this case, even if the types differ only by const qualification. Otherwise the guarantees for unions would effectively imply that the types need to have a common layout if they are standard-layout types (according to a note in 9.5 [class.union] paragraph 1).

In practice I would expect that the two types are laid out identical and that the reinterpret_cast works but there is no guarantee by the standard. Based on your comment MyStringConst merely holds a pointer to the string, i.e., instead of converting to references, I would just return a suitably constructed MyStringConst and avoid relying on undefined behavior:

MyString::operator MyStringConst() const {
    return MyStringConst(str, length);
}

The MyString object still has to live as long as the result from the conversion but this is no different to the case using reinterpret_cast.

BTW, the volatile on the hashCode is ill-advised: the only effect it will have is to slow down the program. I guess you are trying to use it to achieve synchronization between threads but in C++ volatile doesn't help with that at all: you get a data race when writing the member in one thread it is also accessed unsynchronized in another thread. You'd spell the member

std::atomic<int> hashCode;

instead.