UPDATE: I do appreciate \"don\'t want that, want this instead\" suggestions. They are useful, especially when provided in context of the motivating scenari
If I understand you correctly, you have:
NodeBase class that is stateful, and the true workhorse of the system;Accessor types that provide an interface to NodeBase; andNode class which wraps an accessor, presumably providing convenience functions.I assume the last bit because if you don't have a wrapper type that does convenience stuff, then there's no reason not to make the Accessor types your top-level, like you suggested: pass AccessorFoo and AccessorBar around by value. The fact that they aren't the same object is entirely moot; if you think of them like the pointers that they are, then you'll note that &foo != &bar is no more interesting than having NodeBase* p1 = new NodeBase; NodeBase* p2 = p1; and noting that, of course, &p1 != &p2.
If you really do need a wrapper Node and want to make it standard-layout, then I would suggest that you use the statelessness of your Accessor types to your advantage. If they are simply a stateless container of functionality (which they must be; why else would you be able to freely cast them?), then you can do something like this:
struct AccessorFoo {
int getValue(NodeBase* n) { return n->getValueFoo(); }
};
struct AccessorBar {
int getValue(NodeBase* n) { return n->getValueBar(); }
};
template
class Node {
NodeBase* m_node;
public:
int getValue() {
AccessorT accessor;
return accessor.getValue(m_node);
}
};
In this case, you could add a templated conversion operator:
template
operator Node() {
return Node(m_node);
}
And now you've got direct value conversion between any Node type you like.
If you take it just a bit further, you'll make all the methods of the Accessor types static, and arrive at the traits pattern.
The section of the C++ standard that you quoted, incidentally, concerns the behavior of reinterpret_cast in the case that both the source type and the final type are pointers to standard-layout objects, in which case the standard guarantees that you get the same pointer you'd get from casting to a void* and then to the final type. You still don't get to use the object as any type other than the type it was created as without invoking undefined behavior.