I've been refreshing/updating my knowledge of C++ lately, and learning about strict aliasing has made me a bit wary of casting pointers of one type to another. I know that this following code sample works in practice on my compiler, but I want to make sure that it conforms to current standards:
#include <iostream>
using namespace std;
class MyBase {
public:
virtual void DoSomething() = 0;
};
class MyDerived1 : public MyBase {
public:
virtual void DoSomething() {
cout << "I'm #1" << endl;
}
};
class MyDerived2 : public MyBase {
public:
virtual void DoSomething() {
cout << "I'm #2" << endl;
}
};
template <typename Base, typename Member1, typename Member2>
struct Tuple {
public:
Base* Get(int i) {
return &(this->*(lookupTable[i]));
}
private:
Member1 member1;
Member2 member2;
static Base Tuple::* const lookupTable[2];
};
template <typename Base, typename Member1, typename Member2>
Base Tuple<Base, Member1, Member2>::* const Tuple<Base, Member1, Member2>::lookupTable[2] = {
reinterpret_cast<Base Tuple<Base, Member1, Member2>::*>(&Tuple::member1),
reinterpret_cast<Base Tuple<Base, Member1, Member2>::*>(&Tuple::member2)
};
int main() {
Tuple<MyBase, MyDerived1, MyDerived2> tuple;
tuple.Get(0)->DoSomething();
tuple.Get(1)->DoSomething();
return 0;
}
Essentially, this simple tuple contains a pair of elements, each of which should derive from a common base class. The Get function returns a Base*
to the member that the given index represents.
The key part that I'm wondering about is the reinterpret_casts. I know that casting from Derived Struct::*
to Base Struct::*
is generally a no-no, but in this case I only use the pointers-to-member-variable to get a pointer to the object. (I don't try to copy a derived object as though it were a base object, nor stuff a base object into a derived object's memory.) This works as intended on G++, and I just want to be sure that I'm not going to get bitten by any compliant compilers for doing this.
Use of reinterpret_cast
is almost never portable. On top of that, the only valid use of pointer to member casts are the implicit cast from Type Derived::*
to Type Base::*
and careful uses of the static_cast
from Type Base::*
to Type Derived::*
. Since you want to change the type of the member, not the type of the object containing members, this is neither of those.
How about putting tiny functions in that array instead of pointers to members? The following code is tested and should be entirely portable.
#include <iostream>
using namespace std;
class MyBase {
public:
virtual void DoSomething() = 0;
};
class MyDerived1 : public MyBase {
public:
virtual void DoSomething() {
cout << "I'm #1" << endl;
}
};
class MyDerived2 : public MyBase {
public:
virtual void DoSomething() {
cout << "I'm #2" << endl;
}
};
template <typename Base, typename Member1, typename Member2>
struct Tuple {
public:
Base* Get(int i) {
return &(this->*lookupTable[i])();
}
private:
Member1 member1;
Member2 member2;
template <typename MemType, MemType Tuple::*member>
Base& GetMember() { return this->*member; }
typedef Base& (Tuple::*get_member_func)();
static const get_member_func lookupTable[2];
};
template <typename Base, typename Member1, typename Member2>
const typename Tuple<Base, Member1, Member2>::get_member_func
Tuple<Base, Member1, Member2>::lookupTable[2] = {
&Tuple::GetMember<Member1, &Tuple::member1>,
&Tuple::GetMember<Member2, &Tuple::member2>
};
int main() {
Tuple<MyBase, MyDerived1, MyDerived2> tuple;
tuple.Get(0)->DoSomething();
tuple.Get(1)->DoSomething();
return 0;
}