2012-10-20 3 views
16

С блоге Access to private members: Safer nastiness по 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:

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

+0

Кажется странный багом, когда дело доходит до АргументЪ зависимости Lookup (ADL), я в настоящее время на моей ежевике и не могу играть с ним, - но если это компилирует должен (с моей точки зрения) рассматриваться как ошибка. –

+0

Кто-нибудь подтвердил это, чтобы работать с любыми компиляторами, кроме gcc? – hexist

+1

@hexist Я просто проверил это на Clang (3.1) и Intel C++ (13.0.0). –

ответ

6

Вы не вызывающему 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 функция возвращает &A::a, и вы используете a, чтобы прочитать его значение! вот и все, ничего не так, и я думаю, что он будет скомпилирован во всех компиляторах.

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

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's Ok inside a friend function. 
void f() { 
    Rob<A_f, &A::a> r;    // OK 
    typedef Rob<A_f, &A::a> R;  // OK 
} 
+3

вот почему я люблю C++ –

+0

@stackmonster, конечно, у нас есть что-то общее – BigBoss

+0

Однако во всех нормальных условиях вы не можете принимать адрес частного участника, он оборачивается тем, что использует шаблонную обманку. – hexist

0

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

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

это просто ярлык для:

class A 
{ 
    friend void go(); 
}; 

void go() {} 
+2

Но он дружит со стороны A – hexist

+0

, что он имел в виду под глобальным масштабом. –

+0

Вы правы, этот ответ отвечает на вопрос OP о том, почему он может называть 'get' ... хотя скрытый вопрос, который OP должен задать поэтому он может использовать этот трюк для решения '& A :: a' из-за пределов' A'. – hexist

Смежные вопросы