什么是。*&操作员呢?

时间:2015-03-26 22:22:22

标签: c++ c++11 stl

我偶然发现this question,其答案使用了一个奇怪的结构:

typedef std::queue<int> Q;
typedef Q::container_type C;

C & get (Q &q)
{
    struct hack : private Q {
        static C & get (Q &q) {
            return q.*&hack::c;
        }
    };
    return hack::get(q);
}

我通常认为q可以访问由c函数引用的自己的get成员。但是,我无法清楚地解释它。 .*&究竟发生了什么,为什么允许这样做?

2 个答案:

答案 0 :(得分:26)

typedef std::queue<int> Q;

Qqueue改编的容器。

typedef Q::container_type C;

CQ的基础容器 - deque<int>

C & get (Q &q) {

get需要queue并返回deque。实际上它会返回deque包裹的queue:通过传统方式,这是不可能的。

  struct hack : private Q {

hack是函数的本地类型。它继承自Q并且只有一个静态成员函数。从它的名字,你可能会怀疑它是一个黑客。你是对的。

没有hack被实例化。

    static C & get (Q &q) {

hack::getget本身具有相同的签名。事实上,我们将get的所有工作委托给此方法。

      return q.*&hack::c;

这条线需要分解。我将在更多行中完成:

      using mem_ptr_t = C Q::*; // aka typedef C Q::*mem_ptr_t;
      mem_ptr_t c_mem_ptr = &hack::c;
      C& ret = q.*c_mem_ptr;
      return ret;

第一行定义指向CQ类型字段的成员指针的类型。 C ++ 11和C ++ 03命名这种类型的方式都很难看。

第二行获取指向c中字段Q的成员指针。它是通过C ++类型系统中的漏洞实现的。 &hack::c在逻辑上属于C hack::*类型 - 指向C类中类型hack的成员的指针。事实上,这就是我们可以在static成员hack中访问它的原因。但是c实际上是Q,所以C ++中表达式的实际类型是C Q::*:指向成员变量Q的指针。

您无法在hack - &Q::c内直接获取此成员指针是非法的,但&hack::c不是。

您可以将成员指针视为“键入的偏移量”为另一种类型:&hack::ccQ的“偏移量”,同时知道它的类型为{{1} }}。现在这不是真的 - 它是一些不透明的值告诉编译器如何从C获取c - 但它有助于以这种方式思考它(并且可以实现在简单的情况下)。

然后我们将此成员指针与Q一起使用,以从Q&中获取c。获取成员指针受受保护约束:使用它不是!我们这样做的方法是使用运算符Q,它是成员解引用运算符,您可以在右侧传递成员函数指针成员,在左侧传递类实例。 / p>

.*是一个表达式,可在instance .* member_ptr中找到member_ptr所指的成员“指向”。在原始代码中,所有内容都在一行中完成:

instance

所以看起来有一个运算符instance .* &class_name::member_name

.*&

然后我们关闭静态方法和 } }; 类,并且:

hack

打电话给它。此技术可以访问 return hack::get(q); } 状态:没有它,protected成员只能在同一实例的子类中访问。使用此功能,我们可以访问任何实例的protected个成员,而不会违反任何标准。

答案 1 :(得分:8)

这是一个黑客,正如命名法所示。

.*在左侧获取一个对象,在右侧获取一个成员指针,并解析给定对象的指向成员。当然,&是引用运算符; &Class::Member返回一个成员指针,它本身不能被解除引用但可以与.*->*运算符一起使用(后者是所有C ++运算符中最古怪的运算符)。因此obj .* &Class::Memberobj.Member具有完全相同的效果

使用这个更复杂的版本的原因归结为保护语义的漏洞;基本上,它允许访问基类对象的protected成员,即使该对象与执行此脏攻击的类的类型不同。

就个人而言,我认为诀窍太巧妙了一半。我通常会把这样的代码写成:

struct hack : private Q {
    static C & get (Q &q) {
        return static_cast<hack &>(q).c;
    }
};

这在技术上稍微不那么安全,但不会掩盖正在发生的事情。

。*嗯,通常我会避免写这样的东西。但是我今天早些时候做到了这一点,所以我不能扔石头。