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).
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'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);
Let B be derived from class A. By reading various posts I've got an impression that casting like in
const std::shared_ptr<const A> a(new B());
const std::shared_ptr<const B>& b = reinterpret_cast<const std::shared_ptr<const B>&>(a);
is for some reason discouraged and that one should use reinterpret_pointer_cast instead. However, I would like to avoid creating a new shared_ptr for performance reasons. Is the above code legal? Does it lead to undefined behavior? It seems to work in gcc and in Visual Studio.
You want static_pointer_cast.
const std::shared_ptr<const A> a(new B());
const std::shared_ptr<const B> b = std::static_pointer_cast<const B>(a);
I highly doubt the above will cause any performance issues. But if you have evidence that a shared_ptr creates a performance problem, then fallback to the raw pointer:
const B* pB = static_cast<const B*>(a.get());
Another hint. Please try to avoid reinterpret_cast between classes with an inheritance relationship. In cases where there are virtual methods and/or multiple inheritance, the static_cast will correctly adjust the pointer offset to the correct vtable or base offset. But reinterpret_cast will not. (Or technically: undefined behavior)
reinterpret_cast will usually lead to UB. Sometimes you are willing to take the risk of using it, for performances reasons, but you will try to avoid this kind of thing as much as you can. In this case, it better for you to use static_pointer_cast.
Pay attention, that even if you don't know, in this case, which other cast can you use, and you willing to take the risk with reinterpret_cast, you must use some validations after and before the casting- otherwise you will be able to get a lot of errors, and a lot of time spending.
First, you create an object a of type const std::shared_ptr<const A> a and initialize it with a pointer to some type B. This only works if you can assign a B* to an A*, so there should be a relationship such as inheritance. Ignoring this, you convert an object of some type to a reference to another type with reinterpret_cast:
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(x) has the same effect as the conversion
*reinterpret_cast(&x) with the built-in & and * operators (and similarly for reinterpret_cast(x)). —end note ]
For pointers, reinterpret_cast boils down to conversion to void* and then to the target type:
An object pointer can be explicitly converted to an object pointer of
a different type.72 When a prvalue v of object pointer type is
converted to the object pointer type “pointer to cv T”, the result is
static_cast<cv T*>(static_cast<cv void*>(v)).
The semantics of the two static casts are defined as:
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. If the original pointer value represents the address
A of a byte in memory and A satisfies the alignment requirement of T,
then the resulting pointer value represents the same address as the
original pointer value, that is, A. The result of any other such
pointer conversion is unspecified.
The platform I am working on has near and far pointers which are 16 or 32 bit. In that case, the types shared_ptr<A> and shared_ptr<B> are of different size and alignment, and casting one into the other is then unspecified behavior. If alignment matches, the result of the static casts is defined.
However, the first clause about reinterpret_cast to a reference also contains a note
[ 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 ]
so basically, the cast is semantically identical to a pointer conversion with immediate dereferencing. Even if the pointers are of identical size (and compatible alignment), using the casted pointer will violate the strict alias rule since dereferencing is an access.
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:53
— 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 nonstatic
data members (including, recursively, an element or non-static data member of a subaggregate
or contained union),
Using functionalities of a shared_ptr, or any other standard class or template, is only defined when calling the functions (including member functions) for the class of the type which you pass to the function (including as implicit this argument):
Nothing in the standard defines what happens when you call a standard function expecting a Foo and passing a Bar, for any two standard types Foo and Bar (or even for user types.)
That's not defined; that's undefined. By not meeting the most basic precondition: to use arguments of the correct type.
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.