Every time I use reinterpret_cast
I feel like I'm taking a long walk off a short pier.
Is it possible to allow for implicit pointer conversion between templated types with the same underlying structure such as the below?
template <typename T>
struct S {};
int main() {
S<int>* foo = new S<int>;
S<double>* foo1 = foo;
return 1;
}
This gives an error of
<source>: In function 'int main()':
<source>:9:23: error: cannot convert 'S<int>*' to 'S<double>*' in initialization
9 | S<double>* foo1 = foo;
| ^~~
| |
| S<int>*
Which makes since, we never specified any type of conversion operator between the two instances
But I'm having trouble understanding if there is a conversion operator that can allow for implicit conversion between these two types. For instance, below is every constructor, assigner, and conversion operator I can think of and it doesn't seem to do that job
#include <iostream>
struct Arg {};
template <typename T>
struct S {
// All the usually generated constructor/destructor/assigners
S() { puts("\t\tDefault Constructor");}
S(Arg) { puts("\t\tValue Constructor");}
~S() { puts("\t\tDestruct");}
S(const S<T>&) { puts("\t\tCopy construct");}
S(S<T>&&) {puts("\t\tMove construct");}
S<T>& operator=(const S<T>&) {puts("\t\tCopy Assign"); return *this;}
S<T>& operator=(S<T>&&) {puts("\t\tMove Assign"); return *this;}
// Constructors/assigner/conversions to alt S<>
template <typename P>
S(const S<P>&) { puts("\t\tCopy construct");}
template <typename P>
S(S<P>&&) {puts("\t\tMove construct");}
template <typename P>
S<T>& operator=(const S<P>&) {puts("\t\tCopy Assign"); return *this;}
template <typename P>
S<T>& operator=(S<P>&&) {puts("\t\tMove Assign"); return *this;}
template <typename P>
operator S<P>() { puts("Conversion operator"); return S<P>();}
template <typename P>
operator S<P>*() {puts("Pointer Conversion operator"); return new S<P>();}
};
int main() {
S<int>* foo = new S<int>;
S<double>* foo1 = foo;
return 1;
}
But that gives the same error
If I do
int main() {
S<int>* foo = new S<int>;
S<double>* foo1 = *foo;
return 1;
}
That does work by using the pointer conversion operator. Inside of the pointer conversion operator I could use reinterpret_cast<S<P>*>(this)
, though I worry whether that is safe.
And of course reinterpret_cast
works, though reading the rules it's not clear to me whether this is UB.
int main() {
S<int>* foo = new S<int>;
S<double>* foo1 = reinterpret_cast<S<double>*>(foo);
return 1;
}
So if there is a way to make the following valid I'd love to know!
S<int>* foo = new S<int>;
S<double>* foo1 = foo;
I'm using C++14 so anything on a higher standard than that and I'll have a difficult time accepting it.
Thank you for reading this!
Edit:
As Sam points out in the comments I'm really not sure if this is possible, if not then my Q is just whether the reinterpret_cast
is safe in this situation
Edit Edit: For further context, I work on an open source software project where the implementation of our scalar type cannot hold an integral type. So the underlying implementation of S<int>
is the same as S<double>
aka all integral template types are treated by the class as doubles. The implementation S
is used in a PIMPL idiom ala
template <typename T>
class Q {
S<T>* s_type_; // if int T will treat T as a double
// stuff...
template <typename P, enable_if_t<is_convertible_v<P&, T>>* = nullptr>
Q(S<P>* x) : s_type_(x) {} // this line fails for S<double>*
};
This is not the exact code but it's enough to showcase the problem. So the question is pertaining to the constructor for Q
that takes in a pointer to an S<int>
and whether it's valid to assign that pointer to an S<double>
.