Yes, if... (or "Yes, but...") and no otherwise.
The standard specifies (3.7.4.3) the following:
- A pointer value is a safely-derived pointer [...] if it is the result of a well-defined pointer conversion or
reinterpret_cast
of a safely-derived pointer value [or] the result of a reinterpret_cast
of an integer representation of a safely-derived pointer value
- An integer value is an integer representation of a safely-derived pointer [...] if its type is at least as large as
std::intptr_t
and [...] the result of a reinterpret_cast
of a safely-derived pointer value [or]
the result of a valid conversion of an integer representation of a safely-derived pointer value [or] the result of an additive or bitwise operation, one of whose operands is an integer representation of a
safely-derived pointer value
- A traceable pointer object is [...] an object of an integral type that is at least as large as
std::intptr_t
The standard further states that implementations may be relaxed or may be strict about enforcing safely-derived pointers. Which means it is unspecified whether using or dereferencing a not-safely-derived pointer invokes undefined behavior (that's a funny thing to say!)
Which alltogether means no more and no less than "something different might work anyway, but the only safe thing is as specified above".
Therefore, if you either use std::intptr_t
in the first place (the preferrable thing to do!) or if you know that the storage size of whatever integer type you use (say, long
) is at least the size of std::intptr_t
, then it is allowable and well-defined (i.e. "safe") to cast to your integer type and back. The standard guarantees that.
If that's not the case, the conversion from pointer to integer representation will probably (or at least possibly) lose some information, and the conversion back will not give a valid pointer. Or, it might by accident, but this is not guaranteed.
An interesting anecdote is that the C++ standard does not directly define std::intptr_t
at all; it merely says "the same as 7.18 in the C standard".
The C standard, on the other hand, states "designates a signed integer type with the property that any valid
pointer to void can be converted to this type, then converted back to pointer to void, and the result will compare equal to the original pointer".
Which means, without the rather complicated definitions above (in particular the last bit of the first bullet point), it wouldn't be allowable to convert to/from anything but void*
.