来自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
内定义?
修改
我不明白为什么必须将Tag作为参数而不是a.*get<A_f>()
=&GT;好吧,这是由于ADL机制
答案 0 :(得分:7)
您没有从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
}
答案 1 :(得分:1)
这是gcc中已知的编译器错误,并在以后的版本中修复。 看到 - :
答案 2 :(得分:0)
它是合法的,因为友元函数总是在全局范围内,即使您在类中实现它们也是如此。换句话说,这个:
class A
{
friend void go() {}
};
只是一个快捷方式:
class A
{
friend void go();
};
void go() {}