使用模板技巧访问私有成员

时间:2012-10-20 22:21:39

标签: c++ templates

来自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机制

3 个答案:

答案 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内拨打了aa没有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中已知的编译器错误,并在以后的版本中修复。 看到 - :

http://gcc.gnu.org/bugzilla/show_bug.cgi?id=41437

答案 2 :(得分:0)

它是合法的,因为友元函数总是在全局范围内,即使您在类中实现它们也是如此。换句话说,这个:

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

只是一个快捷方式:

class A
{
    friend void go();
};

void go() {}