【问题标题】:access private member using template trick使用模板技巧访问私有成员
【发布时间】:2012-10-20 22:21:38
【问题描述】:

来自Access to private members: Safer nastinessJohannes 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;
}

如何从a 对象调用get 函数,因为它没有在class A 中定义?

编辑:

我不明白为什么 get 必须有 Tag 作为参数而不是 a.*get&lt;A_f&gt;() => 好的,这是由于 ADL 机制

【问题讨论】:

  • 当涉及到参数依赖查找 (ADL) 时,这似乎是一个奇怪的错误,我目前在我的黑莓上,无法使用它 - 但如果编译它应该(来自我的观点)被视为一个错误。
  • @hexist 我刚刚在 Clang (3.1) 和 Intel C++ (13.0.0) 上验证了这一点。
  • 我认为核心问题是为什么编译器不会在template struct Rob&lt;A_f, &amp;A::a&gt;; 上中止类似“A::a 在此上下文中是私有的”之类的内容。毕竟,代码试图获取指向私有成员的指针。很奇怪。
  • @hexist 将 Apple LLVM 4.1 添加到适用的列表中。在这里看到一个模式......
  • @JamesMcNellis,我认为你的链接是错误的。试试看:bloglitb.blogspot.com/2011/12/…

标签: c++ templates


【解决方案1】:

您不是从a 呼叫get!实际上,返回的是一个指向 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;
}

我是从a 内部呼叫p 吗? a 没有p,这正是你的代码中发生的事情,get 函数返回&amp;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
}

【讨论】:

  • @stackmonster 我们当然有共同点
  • 但是在所有正常使用中,您不能获取私人成员的地址,他正在使用模板技巧来解决这个问题。
  • A_f里面的朋友get函数没用吧?
  • @Guillaume07 当然,您可以删除它并仍然得到相同的结果
  • 我不明白为什么 get 应该有 Tag 作为参数
【解决方案2】:

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

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

只是一个快捷方式:

class A
{
    friend void go();
};

void go() {}

【讨论】:

  • 但他是 A 以外的朋友
  • 这就是他所说的全局范围
  • 你是对的,这个答案确实回答了 OP 关于他为什么可以打电话给 get... 虽然 OP 需要问的隐藏问题是为什么他可以使用这个技巧来解决&amp;A::a 来自A 之外。
【解决方案3】:

这是 gcc 中的一个已知编译器错误,已在后续版本中修复。 见-:

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-04-04
    • 1970-01-01
    • 1970-01-01
    • 2017-02-07
    • 2012-12-20
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多