为什么此代码允许访问私有变量

时间:2018-04-10 16:15:27

标签: c++ templates private

我几天前遇到过这种方法(作为C++ course中编程任务的解决方案之一)。

#include <iostream>

struct C {
    C(int i) { m_i = i; }
private:
    int m_i;
};

template<typename Tag, int C::* M>
struct Rob {
    friend
    int C::* get(Tag) {
        return M;
    }
};

template<typename D>
struct AcessorTag {
    typedef D C::*type;
};

template struct Rob<AcessorTag<int>, &C::m_i>;

int &get_c(C &cls) {
    return cls.*get(AcessorTag<int>());
}

int main()
{
    C c(13);
    std::cout << get_c(c);
    return 0;
}

你能解释为什么这段代码会编译吗?几乎所有这里发生的事情都是1)我们将指向成员的指针作为参数传递给结构模板2)我们声明了一个只返回指向成员的指针的友元函数。

这是标准的C ++吗? (我已经在VS 2015上测试过了)

get函数是struct Rob<>的朋友,但它不是struct C的一部分。无论如何,struct C似乎没有朋友,那么为什么私人会员可以访问呢?

谢谢!

2 个答案:

答案 0 :(得分:1)

除了缺少get函数的前向声明之外,此代码应该正常工作并且符合标准。此代码有效,因为应用了模板的显式实例化定义的特殊规则:

  

17.8.2显式实例化[temp.explicit]

     

14通常的访问检查规则不适用于用于指定显式实例化的名称。 [注意:特别是,函数声明符中使用的模板参数和名称(包括参数类型,返回类型和异常规范)可能是通常不可访问的私有类型或对象,模板可能是成员模板或成员函数通常无法访问。 - 后注]

所以写作

template struct Rob<AcessorTag<int>, &C::m_i>;

将绕过通常的访问检查,并向get函数体提供指向其他无法访问的成员的指针。

this answer已经很好地描述了这种花哨规则的动机。

答案 1 :(得分:-1)

当然,你的代码不能用gnu c ++和clang ++编译;标识符get不是C的成员。即使您尝试实例化Rob,例如

Rob<AcessorTag<int>, &C::m_i> rob;

你获得了

error: 'int C::m_i' is private.

如果他们的c ++编译器符合任何标准,您可以随时询问Microsoft。如果他们回答的话,让我知道他们的答案。