I'm trying to replace a type, typedef
'ed from a built-in integral type, used in a large project with a custom class, which would implement some additional functionality like avoiding truncation on assignment etc. But the project extensively uses a conversion like reinterpret_cast<void*>(value)
. I've done a naive attempt at implementing an operator void*()
in my new class, but apparently this doesn't make it possible to reinterpret_cast
, only allows to static_cast
to void*
. Here's the code:
#include <cstdlib>
#include <type_traits>
#define USE_NEW_VALUE_TYPE
#ifdef USE_NEW_VALUE_TYPE
struct Value
{
size_t value;
operator void* () { return reinterpret_cast<void*>(value); }
};
static_assert(std::is_pod<Value>::value,"Value is not POD");
#else
typedef size_t Value;
#endif
int main()
{
Value v{5};
void* p=reinterpret_cast<void*>(v); // This is how the project uses it
}
I thought that if the class is POD, this would allow me to do such things like a reinterpret_cast
. But this code gives me a compilation error:
invalid cast from type ‘Value’ to type ‘void*’
So my question is then: how do I add support for such a reinterpret_cast
, so that I didn't have to manually switch it to static_cast
in every part of the project?
Unfortunately, reinterpret_cast
is not designed for this. The reason this works for POD types is #3 on the cppreference website:
A value of any integral or enumeration type can be converted to a pointer type. A pointer converted to an integer of sufficient size and back to the same pointer type is guaranteed to have its original value, otherwise the resulting pointer cannot be dereferenced safely (the round-trip conversion in the opposite direction is not guaranteed; the same pointer may have multiple integer representations) The null pointer constant NULL or integer zero is not guaranteed to yield the null pointer value of the target type; static_cast or implicit conversion should be used for this purpose.
And, I believe §12.3.2 plays a role here:
A conversion function is never used to convert a (possibly cv-qualified) object to the (possibly cv-qualified) same object type (or a reference to it), to a (possibly cv-qualified) base class of that type (or a reference to it), or to (possibly cv-qualified) void.
In short: User-defined conversions do not participate in resolution of reinterpret_casts
.
Possible solutions
1. Explicitly take the address of v:
void* p=reinterpret_cast<void*>(&v);
2. Defined operator void*()
as you did, you could write
void *p = v;
Note: This probably opens up a wormhole of problems due to unwanted implicit conversions
3. Use static_cast
as you said yourself.
Note: use &v
rather than defining operator void*()
for the same reason as in No. 2
4. Ideally, but probably unrealistic, fix the underlying design issue requiring casting classes to void
No. 3 might be the least painful solution here.
EDIT for comment
There are two ways here:
1. Use an overloaded address-of operator (operator&
). This may not be possible depending on how Value
is used.
#include <cstdlib>
struct Value
{
size_t value;
void* operator &() { return reinterpret_cast<void*>(value); }
};
int main()
{
Value v;
void* p=reinterpret_cast<void*>(&v); // This is how the project uses it
}
2. Implement an operator uintptr_t
. While this requires verbose double-casting, it transfers the conversion into the Value
class, and uintptr_t
is guaranteed to be able to hold a void*
.
#include <cstdlib>
struct Value
{
size_t value;
operator uintptr_t() { return static_cast<uintptr_t>(value); }
};
int main()
{
Value v;
void* p=reinterpret_cast<void*>(static_cast<uintptr_t>(v)); // This is how the project uses it
// or less verbose
void* p2=reinterpret_cast<void*>(uintptr_t(v));
}