It is technically undefined behavior to use a different sequence of tokens to define the same entity (here a class) in different Translation Units.
Whatever the technic you use, as long as it alters the sequence of tokens composing it, it is evil from the Standard point of view (though likely to work in practice).
Johannes discovered a way to do so while respecting the Standard. It is based on the fact that even though a
is a private attribute in class A
, &A::a
can be written in contexts that cannot write A.a
(perhaps an oversight in the Standard ?).
Core method:
template<typename Tag, typename Tag::type M>
struct Rob {
friend typename Tag::type get(Tag) {
return M;
}
};
// use
struct A {
A(int a):a(a) { }
private:
int a;
};
// tag used to access A::a
struct A_f {
typedef int A::*type;
friend type get(A_f);
};
template struct Rob<A_f, &A::a>;
int main() {
A a(42);
std::cout << "proof: " << a.*get(A_f()) << std::endl;
}
Extension for simplicity:
template<typename Tag, typename Member>
struct TagBase {
typedef Member type;
friend type get(Tag);
};
struct A_f : TagBase<A_f, int A::*> { };
EDIT:
This trick is (amusingly) explicitly allowed by the Standard
§14.7.2/12 The usual access checking rules do not apply to names used to specify explicit instantiations. [...]
#define private public
– Gudgeonprivate
is a reserved identifier (a compiler could produce an error and refuse to compile or silently ignore your directive). Second, the layout of a class is dependent on the access level of the members. Though it is a rarely use feature, you could introduce subtle bugs by having different parts of the program expecting a different memory layout for the same object. – Singapore