我几天前遇到过这种方法(作为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
似乎没有朋友,那么为什么私人会员可以访问呢?
谢谢!
答案 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。如果他们回答的话,让我知道他们的答案。