I've seen reinterpret_cast used to apply incrementation to enum classes, and I'd like to know if this usage is acceptable in standard C++.
enum class Foo : int8_t
{
Bar1,
Bar2,
Bar3,
Bar4,
First = Bar1,
Last = Bar4
};
for (Foo foo = Foo::First; foo <= Foo::Last; ++reinterpret_cast<int8_t &>(foo))
{
...
}
I know casting to a reference of a base class is safe in case of trivial classes. But since enum classes are not event implicitly converted to their underlying types, I'm not sure if and how the code above would be guaranteed to work in all compilers. Any clues?
You might want to overload operator ++ for your enum if you really want to iterate its values:
Foo& operator++( Foo& f )
{
using UT = std::underlying_type< Foo >::type;
f = static_cast< Foo >( static_cast< UT >( f ) + 1 );
return f;
}
and use
for (Foo foo = Foo::First; foo <= Foo::Last; ++foo)
{
...
}
To answer the question of whether or not the reinterpret_cast is allowed, it all starts with 5.2.10/1:
5.2.10 Reinterpret cast [expr.reinterpret.cast]
1 The result of the expression reinterpret_cast<T>(v) is the result of converting the expression v to type T. If T is an lvalue reference type or an rvalue reference to function type, the result is an lvalue; if T is an rvalue reference to object type, the result is an xvalue; otherwise, the result is a prvalue and the lvalue-to-rvalue (4.1), array-to-pointer (4.2), and function-to-pointer (4.3) standard conversions are performed on the expression v. Conversions that can be performed explicitly using reinterpret_cast are listed below. No other conversion can be performed explicitly using reinterpret_cast.
(emphasis mine)
The reinterpretation using references is based on pointers as per 5.2.10/11:
11 A glvalue expression of type T1 can be cast to the type “reference to T2” if an expression of type “pointer to T1” can be explicitly converted to the type “pointer to T2” using a reinterpret_cast. The result refers to the same object as the source glvalue, but with the specified type. [ Note: That is, for lvalues, a reference cast reinterpret_cast<T&>(x) has the same effect as the conversion *reinterpret_cast<T*>(&x) with the built-in & and * operators (and similarly for reinterpret_cast<T&&>(x)). — end note ] No temporary is created, no copy is made, and constructors (12.1) or conversion functions (12.3) are not called.
Which transforms the question from this:
reinterpret_cast<int8_t&>(foo)
to whether this is legal:
*reinterpret_cast<int8_t*>(&foo)
Next stop is 5.2.10/7:
7 An object pointer can be explicitly converted to an object pointer of a different type. When a prvalue v of type “pointer to T1” is converted to the type “pointer to cv T2”, the result is static_cast<cv T2*>(static_cast<cv void*>(v)) if both T1 and T2 are standard-layout types (3.9) and the alignment requirements of T2 are no stricter than those of T1, or if either type is void. Converting a prvalue of type “pointer to T1” to the type “pointer to T2” (where T1 and T2 are object types and where the alignment requirements of T2 are no stricter than those of T1) and back to its original type yields the original pointer value. The result of any other such pointer conversion is unspecified.
Given 3.9/9 both int8_t and your enumeration type are standard layout types the question now transformed into:
*static_cast<int8_t*>(static_cast<void*>(&foo))
This is where you are out of luck. static_cast is defined in 5.2.9 and there is nothing which makes the above legal - in fact 5.2.9/5 is a clear hint that it is illegal. The other clauses don't help:
5.2.9/13 requires T* -> void* -> T* where T must be identical (omitting cv)
5.2.9/9 and 5.2.9/10 are not about pointers, but about values
5.2.9/11 is about classes and class hierarchies
5.2.9/12 is about class member pointers
My conclusion from this is that your code
reinterpret_cast<int8_t&>(foo)
is not legal, its behavior is not defined by the standard.
Also note that the above mentioned 5.2.9/9 and 5.2.9/10 are responsible for making the code legal which I gave in the initial answer and which you can still find at the top.
The increment accesses the value of foo through an lvalue of a different type, which is undefined behaviour except in the cases listed in 3.10 [basic.lval]. Enumeration types and their underlying types are not in that list, so the code has undefined behaviour.
With some compilers that support non-standard extensions you can do it via type-punning:
union intenum
{
int8_t i;
Foo e;
};
intenum ie;
for (ie.e = Foo::First; ie.e <= Foo::Last; ++ie.i)
// ...
but this is not portable either, because accessing intenum::i after storing a value in intenum::e is not allowed by the standard.
But why not just use an integer and convert as needed?
for (int8_t i = static_cast<int8_t>(Foo::First);
i <= static_cast<int8_t>(Foo::Last);
++i)
{
Foo e = static_cast<Foo>(i);
// ...
}
This is portable and safe.
(It's still not a good idea IMHO, because there could be several enumerators with the same value, or values of the enumeration type that have no corresponding enumerator label.)
It is safe as long as it casts to the exact underlying type of the enum.
If the underlying type of the enum class changes that ++reinterpret_cast<int8_t &>(foo) breaks silently.
A safer version:
foo = static_cast<Foo>(static_cast<std::underlying_type<Foo>::type>(foo) + 1);
Or,
++reinterpret_cast<std::underlying_type<Foo>::type&>(foo);
Related
To my understanding of the standard regarding [expr.reinterpret.cast]—but please correct me if I'm wrong—converting a pointer to member function to some other pointer to member function and back to its original is actually legal, well-defined behavior. As laid out in section 7.6.1.10:
A prvalue of type “pointer to member of X of type T1” can be
explicitly converted to a prvalue of a different type “pointer to
member of Y of type T2” if T1 and T2 are both function types or both
object types.59 The null member pointer value ([conv.mem]) is
converted to the null member pointer value of the destination type.
The result of this conversion is unspecified, except in the following
cases:
(10.1) Converting a prvalue of type “pointer to member function” to a
different pointer-to-member-function type and back to its original
type yields the original pointer-to-member value.
(10.2) Converting a prvalue of type “pointer to data member of X of
type T1” to the type “pointer to data member of Y of type T2” (where
the alignment requirements of T2 are no stricter than those of T1) and
back to its original type yields the original pointer-to-member value.
This seems to be contrary to regular function pointers, where doing the same conversions would result in unspecified behavior. As laid out in section 7.6.1.6:
A function pointer can be explicitly converted to a function pointer
of a different type. [Note 4: The effect of calling a function through
a pointer to a function type ([dcl.fct]) that is not the same as the
type used in the definition of the function is undefined
([expr.call]). — end note] Except that converting a prvalue of type
“pointer to T1” to the type “pointer to T2” (where T1 and T2 are
function types) and back to its original type yields the original
pointer value, the result of such a pointer conversion is unspecified.
[Note 5: See also [conv.ptr] for more details of pointer conversions.
— end note]
Some example code to illustrate the distinction between the two types of conversions:
struct s { auto f() -> void {} };
struct t { auto g(t) -> t { return {}; } };
auto f() -> void {};
auto g(t) -> t { return {}; }
auto main() -> int {
auto ps = &s::f;
auto pt = reinterpret_cast<decltype(&t::g)>(ps);
(s{}.*reinterpret_cast<decltype(&s::f)>(pt))(); // well-defined behavior
auto pf = &f;
auto pg = reinterpret_cast<decltype(&g)>(pf);
reinterpret_cast<decltype(&f)>(pg)(); // unspecified behavior
}
What is the reason behind having the regular function pointer conversions result in unspecified behavior? Why aren't both types of conversions either well-defined or unspecified? What are the ramifications of allowing regular function pointer conversions to be well-defined as well? It strikes me as quite odd to have this distinction when both types of conversions appear to be of such a similar nature.
You simply misread the second quote. In the highlighted sentence everything in the subordinate clause before the main clause "the result of such a pointer conversion is unspecified" is stating an exception to that main statement.
The reinterpret_cast round-trip conversion guarantee works for function pointers exactly like it does for member function pointers (but not for object pointers or data member pointers, where it is restricted by alignment).
So both of the examples you give result in specified function/member pointer values after the back-reinterpret_cast and the program has well-defined behavior.
The second quote is simply using a more compact wording to say that aside from this use of the result of the conversion, nothing else is specified about reinterpret_cast<decltype(&g)>(pf).
The same is true for reinterpret_cast<decltype(&t::g)>(ps). The relevant statement for this is in the first quote before the highlighted section: "The result of this conversion is unspecified, except in the following cases:"
I have a class template A which contains a container of pointers (T*):
template <typename T>
class A {
public:
// ...
private:
std::vector<T*> data;
};
and a bunch of functions like:
void f(const A<const T>&);
void g(const A<const T>&);
Is it OK to call these functions via a cast from A<const T> to A<T>?
A<double> a;
...
auto& ac = reinterpret_cast<const A<const double>&>(a);
f(ac);
I'm pretty sure that this code has undefined behaviour.
Is it dangerous to use such conversions in real life?
Although the reinterpret_cast itself might be unspecified behaviour, attempting to access the parameters once you've done the cast is undefined behaviour.
N3337 [basic.lval]/10:
If a program attempts to access the stored value of an object through a glvalue of other than one of the following types the behavior is undefined
— the dynamic type of the object,
— a cv-qualified version of the dynamic type of the object,
— a type similar (as defined in 4.4) to the dynamic type of the object,
— a type that is the signed or unsigned type corresponding to the dynamic type of the object,
— a type that is the signed or unsigned type corresponding to a cv-qualified version of the dynamic type
of the object,
— an aggregate or union type that includes one of the aforementioned types among its elements or non-
static data members (including, recursively, an element or non-static data member of a subaggregate
or contained union),
— a type that is a (possibly cv-qualified) base class type of the dynamic type of the object,
— a char or unsigned char type.
Your example is none of the above.
As A<double> and A<const double> are unrelated types, it's actually unspecified (originally I thought undefined) behavior and correspondingly yes it's a bad idea to use in real life: You never know what system(s) or compiler(s) you may port to that change the behavior is strange ways.
Reference:
5.2.10/11:
An lvalue expression of type T1 can be cast to the type “reference to
T2” if an expression of type “pointer to T1” can be explicitly
converted to the type “pointer to T2” using a reinterpret_cast. That
is, a reference cast reinterpret_cast(x) has the same effect as
the conversion *reinterpret_cast(&x) with the built-in & and *
operators (and similarly for reinterpret_cast(x)).
So they've redirected us to an earlier section 5.2.10/7:
An object pointer can be explicitly converted to an object pointer of
a different type. ... ... Converting a prvalue of type
“pointer to T1” to the type “pointer to T2” (where T1 and T2 are
object types and where the alignment requirements of T2 are no
stricter than those of T1) and back to its original type yields the
original pointer value. The result of any other such pointer
conversion is unspecified.
If f and g are algorithms that work on containers, the easy solution is to change them to template algorithms that work on ranges (iterator pairs).
#include <cassert>
struct A { int a; };
struct I1 : A { int a; };
struct I2 : A { int a; };
struct D : I1, I2 { int a; };
using namespace std;
int main()
{
auto d = new D;
auto a = static_cast<I2*>(d);
assert((void*)(a) != (void*)(d)); // OK
auto b = reinterpret_cast<I2*>(d);
assert((void*)(b) == (void*)(d)); // OK under VC++. Is it guaranteed?
}
Does reinterpret_cast guarantee it will never change the value of its operand?
Does reinterpret_cast guarantee it will never change the value of its operand?
TL;DR I think Yes, under some conditions.
The guarantee should hold as long as the destination type has an alignment requirement no stricter than the source type. Otherwise the effect of the conversion is unspecified.
The simplest way is to ask the Standard, and specifically: §5.2.10 [expr.reinterpret.cast]
7/ An object pointer can be explicitly converted to an object pointer of a different type.70 When a prvalue v of type “pointer to T1” is converted to the type “pointer to cv T2”, the result is static_cast<cv T2*>(static_cast<cv void*>(v)) if both T1 and T2 are standard-layout types (3.9) and the alignment requirements of T2 are no stricter than those of T1, or if either type is void. Converting a prvalue of type “pointer to T1” to the type “pointer to T2” (where T1 and T2 are object types and where the alignment requirements of T2 are no stricter than those of T1) and back to its original type yields the original pointer value. The result of any other such pointer conversion is unspecified.
In your case since there is no virtual method in this hierarchy, the types are standard-layout types. And since indeed both have the same alignment requirements (as they contain the same values), then indeed we match the well-specified effects here and thus:
reinterpret_cast<I2*>(d)
is equivalent to:
static_cast<I2*>(static_cast<void*>(d))
Thus we need go back to §5.2.9 [expr.static.cast]:
7/ The inverse of any standard conversion sequence (Clause 4) not containing an lvalue-to-rvalue (4.1), array-to-pointer (4.2), function-to-pointer (4.3), null pointer (4.10), null member pointer (4.11), or boolean (4.12) conversion, can be performed explicitly using static_cast. A program is ill-formed if it uses static_cast to perform the inverse of an ill-formed standard conversion sequence.
T* to void* is a standard conversion sequence according to §4.10 [conv.ptr], paragraph 2; so I suppose this means that a static_cast from void* to T* should yield the same address (supposing it matches the alignment requirements of T in the first place).
13/ A prvalue of type “pointer to cv1 void” can be converted to a prvalue of type “pointer to cv2 T,” where T is an object type and cv2 is the same cv-qualification as, or greater cv-qualification than, cv1. The null pointer value is converted to the null pointer value of the destination type. A value of type pointer to object converted to “pointer to cv void” and back, possibly with different cv-qualification, shall have its original value. [ Example:
T* p1 = new T;
const T* p2 = static_cast<const T*>(static_cast<void*>(p1));
bool b = p1 == p2; // b will have the value true.
—end example ]
Unfortunately, the example is off (compared to your case); so we don't get much from this. I just cited it for completeness since it was related.
No.
Well I have to type some umpteen characters here to satisfy SO sillyrules, but the answer's still the same. However, I might use this extra text to mention that of course the effective guaranteees you have can be more practical than what the holy C++ standard offers. But then, that's evident to anyone with a brain (as programmers are wont to have), so it might sound a bit patronizing to mention that, but on the third hand, this should be enough text for SO.
Oh, now looking at your code, I think the first assertion should not hold (in general), because as I recall nothing is guaranteed about the memory order of base class sub-objects. You might want to check that. Also in practice by doing the same with other base class.
I've seen reinterpret_cast used to apply incrementation to enum classes, and I'd like to know if this usage is acceptable in standard C++.
enum class Foo : int8_t
{
Bar1,
Bar2,
Bar3,
Bar4,
First = Bar1,
Last = Bar4
};
for (Foo foo = Foo::First; foo <= Foo::Last; ++reinterpret_cast<int8_t &>(foo))
{
...
}
I know casting to a reference of a base class is safe in case of trivial classes. But since enum classes are not event implicitly converted to their underlying types, I'm not sure if and how the code above would be guaranteed to work in all compilers. Any clues?
You might want to overload operator ++ for your enum if you really want to iterate its values:
Foo& operator++( Foo& f )
{
using UT = std::underlying_type< Foo >::type;
f = static_cast< Foo >( static_cast< UT >( f ) + 1 );
return f;
}
and use
for (Foo foo = Foo::First; foo <= Foo::Last; ++foo)
{
...
}
To answer the question of whether or not the reinterpret_cast is allowed, it all starts with 5.2.10/1:
5.2.10 Reinterpret cast [expr.reinterpret.cast]
1 The result of the expression reinterpret_cast<T>(v) is the result of converting the expression v to type T. If T is an lvalue reference type or an rvalue reference to function type, the result is an lvalue; if T is an rvalue reference to object type, the result is an xvalue; otherwise, the result is a prvalue and the lvalue-to-rvalue (4.1), array-to-pointer (4.2), and function-to-pointer (4.3) standard conversions are performed on the expression v. Conversions that can be performed explicitly using reinterpret_cast are listed below. No other conversion can be performed explicitly using reinterpret_cast.
(emphasis mine)
The reinterpretation using references is based on pointers as per 5.2.10/11:
11 A glvalue expression of type T1 can be cast to the type “reference to T2” if an expression of type “pointer to T1” can be explicitly converted to the type “pointer to T2” using a reinterpret_cast. The result refers to the same object as the source glvalue, but with the specified type. [ Note: That is, for lvalues, a reference cast reinterpret_cast<T&>(x) has the same effect as the conversion *reinterpret_cast<T*>(&x) with the built-in & and * operators (and similarly for reinterpret_cast<T&&>(x)). — end note ] No temporary is created, no copy is made, and constructors (12.1) or conversion functions (12.3) are not called.
Which transforms the question from this:
reinterpret_cast<int8_t&>(foo)
to whether this is legal:
*reinterpret_cast<int8_t*>(&foo)
Next stop is 5.2.10/7:
7 An object pointer can be explicitly converted to an object pointer of a different type. When a prvalue v of type “pointer to T1” is converted to the type “pointer to cv T2”, the result is static_cast<cv T2*>(static_cast<cv void*>(v)) if both T1 and T2 are standard-layout types (3.9) and the alignment requirements of T2 are no stricter than those of T1, or if either type is void. Converting a prvalue of type “pointer to T1” to the type “pointer to T2” (where T1 and T2 are object types and where the alignment requirements of T2 are no stricter than those of T1) and back to its original type yields the original pointer value. The result of any other such pointer conversion is unspecified.
Given 3.9/9 both int8_t and your enumeration type are standard layout types the question now transformed into:
*static_cast<int8_t*>(static_cast<void*>(&foo))
This is where you are out of luck. static_cast is defined in 5.2.9 and there is nothing which makes the above legal - in fact 5.2.9/5 is a clear hint that it is illegal. The other clauses don't help:
5.2.9/13 requires T* -> void* -> T* where T must be identical (omitting cv)
5.2.9/9 and 5.2.9/10 are not about pointers, but about values
5.2.9/11 is about classes and class hierarchies
5.2.9/12 is about class member pointers
My conclusion from this is that your code
reinterpret_cast<int8_t&>(foo)
is not legal, its behavior is not defined by the standard.
Also note that the above mentioned 5.2.9/9 and 5.2.9/10 are responsible for making the code legal which I gave in the initial answer and which you can still find at the top.
The increment accesses the value of foo through an lvalue of a different type, which is undefined behaviour except in the cases listed in 3.10 [basic.lval]. Enumeration types and their underlying types are not in that list, so the code has undefined behaviour.
With some compilers that support non-standard extensions you can do it via type-punning:
union intenum
{
int8_t i;
Foo e;
};
intenum ie;
for (ie.e = Foo::First; ie.e <= Foo::Last; ++ie.i)
// ...
but this is not portable either, because accessing intenum::i after storing a value in intenum::e is not allowed by the standard.
But why not just use an integer and convert as needed?
for (int8_t i = static_cast<int8_t>(Foo::First);
i <= static_cast<int8_t>(Foo::Last);
++i)
{
Foo e = static_cast<Foo>(i);
// ...
}
This is portable and safe.
(It's still not a good idea IMHO, because there could be several enumerators with the same value, or values of the enumeration type that have no corresponding enumerator label.)
It is safe as long as it casts to the exact underlying type of the enum.
If the underlying type of the enum class changes that ++reinterpret_cast<int8_t &>(foo) breaks silently.
A safer version:
foo = static_cast<Foo>(static_cast<std::underlying_type<Foo>::type>(foo) + 1);
Or,
++reinterpret_cast<std::underlying_type<Foo>::type&>(foo);
Where in the C++ spec is this allowed? It's cool. I want to know how this is spec'd. I didn't realize the spec allowed having a pointer to a undefined type.
class T;
int main(int argc, char* argv[])
{
int x;
T* p = reinterpret_cast<T*>(&x);
return 0;
}
What you're doing may be legal, but that depends on the real definition of class T, so we can't know for sure based on the code you've shown.
Starting with C++11 §3.9.2/3:
Pointers to incomplete types are allowed although there are restrictions on what can be done with them.
Those restrictions are listed in §3.2/4:
A class type T must be complete if:
an object of type T is defined, or
a non-static class data member of type T is declared, or
T is used as the object type or array element type in a new-expression, or
an lvalue-to-rvalue conversion is applied to a glvalue referring to an object of type T, or
an expression is converted (either implicitly or explicitly) to type T, or
an expression that is not a null pointer constant, and has type other than void*, is converted to the type pointer to T or reference to T using an implicit conversion, a dynamic_cast or a static_cast, or
a class member access operator is applied to an expression of type T, or
the typeid operator or the sizeof operator is applied to an operand of type T, or
a function with a return type or argument type of type T is defined or called, or
a class with a base class of type T is defined, or
an lvalue of type T is assigned to, or
the type T is the subject of an alignof expression, or
an exception-declaration has type T, reference to T, or pointer to T.
The 6th bullet appears pertinent here, as we can see in §5.2.10/7 that a reinterpret_cast between pointer types is defined in terms of static_cast:
An object pointer can be explicitly converted to an object pointer of a different type. When a prvalue v of type “pointer to T1” is converted to the type “pointer to cv T2”, the result is static_cast<cv T2*>(static_cast<cv void*>(v)) if both T1 and T2 are standard-layout types and the alignment requirements of T2 are no stricter than those of T1, or if either type is void. Converting a prvalue of type “pointer to T1” to the type “pointer to T2” (where T1 and T2 are object types and where the alignment requirements of T2 are no stricter than those of T1) and back to its original type yields the original pointer value. The result of any other such pointer conversion is unspecified.
But because reinterpret_cast static_casts to void* first, then to the real resulting pointer type, that 6th bullet doesn't apply.
So, so far you're still in legal territory, despite T being an incomplete type. However, if it turns out that T is not a standard-layout type or has stricter alignment requirements than int, then the last sentence in §5.2.10/7 holds true and you're invoking UB.
The only thing you're allowed to do after casting a pointer to an unrelated type (other than char*), is cast back to the original pointer type.
#cli_hlt beat me to providing the section.
Here's the rule itself:
An object pointer can be explicitly converted to an object pointer of a different type.
When a prvalue v of type "pointer to T1" is converted to the type "pointer to cv T2", the result is static_cast<cv T2*>(static_cast<cv void*>(v)) if both T1 and T2 are standard-layout types (3.9) and the alignment requirements of T2 are no stricter than those of T1, or if either type is void. Converting a prvalue of type
"pointer to T1" to the type "pointer to T2" (where T1 and T2 are object types and where the alignment requirements of T2 are no stricter than those of T1) and back to its original type yields the original pointer value. The result of any other such pointer conversion is unspecified.
The strict aliasing rule prohibits access to an object through an unrelated type. See https://stackoverflow.com/a/7005988/103167
Another rule somewhat related to your question is found in section 5.4:
The operand of a cast using the cast notation can be a prvalue of type "pointer to incomplete class type". The destination type of a cast using the cast notation can be "pointer to incomplete class type" If both the operand and destination types are class types and one or both are incomplete, it is unspecified whether the static_cast or the reinterpret_cast interpretation is used, even if there is an inheritance relationship
between the two classes.
Section 5.2.10 (7) for your case (as of ISO/IEC14882:1998(E), as well as in the 2011 FDIS).
I'm not sure what you perceive as being allowed. There is a guarantee that you can reinterpret_cast from one pointer type to a sufficiently big other pointer type and back to the original type again and will be the original pointer. The specification for this is in 5.2.10 [expr.reinterpret.cast]. That is, the following is guaranteed to work:
T* ptr = ...;
S* sptr = reinterpret_cast<S*>(ptr);
T* tptr = reinterpret_cast<T*>(sptr);
assert(ptr == tptr);
We had an out of session discussion earlier this week on this topic: if the Death Station 9000 implementation (which would be a conforming implementation of C++ but also tries to break user code wherever it is allowed to do so) XORs the bit pattern of the pointer with a bit pattern randomly chosen at the beginning of the program execution, would this be a permissible implementation if the types involved in the reinterpret_cast<T>(x) are not involving char. The consensus was that this would be OK.