Технически неопределенное поведение использовать другую последовательность токенов для определения одного и того же объекта (здесь класса) в разных единицах перевода.
Какой бы техникой вы ни пользовались, пока она меняет последовательность токенов, составляющих ее, она является злом со стандартной точки зрения (хотя, вероятно, сработает на практике).
Йоханнес открыл способ сделать это, соблюдая Стандарт. Он основан на том факте, что, хотя a
является частным атрибутом в классе A
, &A::a
может быть записан в контекстах, которые не могут записать A.a
(возможно, упущение в Стандарте?).
Основной метод:
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;
}
Расширение для простоты:
template<typename Tag, typename Member>
struct TagBase {
typedef Member type;
friend type get(Tag);
};
struct A_f : TagBase<A_f, int A::*> { };
EDIT
Этот трюк (забавно) явно разрешен Стандартом
§14.7.2 / 12 Обычные правила проверки доступа не применяются к именам, используемым для указания явных экземпляров. [...]