Получить доступ к частному члену, используя шаблонный трюк - программирование
Подтвердить что ты не робот

Получить доступ к частному члену, используя шаблонный трюк

Из сообщения в блоге Доступ к закрытым членам: более безопасная гадость от Johannes Schaub - litb:

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;
}

как функция get может быть вызвана из объекта a, так как она не определена внутри class A?

EDIT:

Я не понимаю, почему get должен иметь тег как параметр вместо a.*get<A_f>() = > ok из-за механизма ADL

4b9b3361

Ответ 1

Вы не вызываете get из a! Фактически, то, что получает возврат, является указателем класса на член внутри a, а тип его - int A::*, поэтому для доступа к этому значению требуется экземпляр a.

Например, позвольте мне немного поиграть с вашим кодом:

struct A {
    A(int a):a(a) { }
    int b;
private:
    int a;
};
void test() {
    auto p = &A::b;
    std::cout << a.*p << std::endl;
}

Я звонил p изнутри a? a не имеет p, это именно то, что произошло в вашем коде, get function return &A::a, и вы используете a, чтобы прочитать его значение! что все, ничего не так, и я думаю, что он будет скомпилирован во всех компиляторах.

Еще один вопрос: почему С++ разрешает объявление шаблона с использованием частного члена a. Стандарт С++ говорит:

14.7.2p8. Обычные правила проверки доступа не применяются к именам, используемым для указания явных экземпляров. [Примечание: в частности, шаблон аргументы и имена, используемые в деклараторе функции (включая типы параметров, типы возвращаемых данных и спецификации исключений) могут быть частные типы или объекты, которые обычно не будут доступны и шаблон может быть шаблоном-членом или функцией-членом, которая обычно не доступны.]

Но если вы попытаетесь создать экземпляр или даже typedef указанный шаблон, вы получите сообщение об ошибке. Немного измените свой пример:

struct A {
private:
    int a;
    friend void f();
};

// Explicit instantiation - OK, no access checks
template struct Rob<A_f, &A::a>;

// Try to use the type in some way - get an error.
struct Rob<A_f, &A::a> r;            // error
typedef struct Rob<A_f, &A::a> R;    // error
void g(struct Rob<A_f, &A::a>);      // error

// However, it Ok inside a friend function.
void f() {
    Rob<A_f, &A::a> r;               // OK
    typedef Rob<A_f, &A::a> R;       // OK
}

Ответ 3

Это законно, потому что функции друзей всегда находятся в глобальной области, даже если вы реализуете их внутри класса. Другими словами, это:

class A
{
    friend void go() {}
};

является просто ярлыком для:

class A
{
    friend void go();
};

void go() {}