我希望能够限制一个类的字段的访问(或修改),使其只能通过专用函数/方法从给定的命名空间中访问(或设置)。
由于友元关系不具有传递性或继承性,我的想法是使用内部命名空间(应该是实现细节的命名空间)并将相应的函数设为友元。例如,如果我们想要存储一个秘密,并且只允许内部命名空间中的代码访问它,而其他人则可以设置它,我希望这个做法可以解决问题:
然而,看起来像是
由于友元关系不具有传递性或继承性,我的想法是使用内部命名空间(应该是实现细节的命名空间)并将相应的函数设为友元。例如,如果我们想要存储一个秘密,并且只允许内部命名空间中的代码访问它,而其他人则可以设置它,我希望这个做法可以解决问题:
#include <cassert>
namespace detail {
// forward declaration doesn't help
// class SecretHolder;
// int GetSecret(SecretHolder& sh);
class SecretHolder {
int secret_;
friend int GetSecret(SecretHolder& sh);
public:
void SetSecret(int val) { secret_ = val; }
};
// nor does inlining as "friend void GetSecret(SecretHolder& sh) { ... }"
int GetSecret(SecretHolder& sh) { return sh.secret_; }
void DoNothing() { }
} // namespace detail
class User : public detail::SecretHolder {
// ...
};
int main(int argc, char* argv[]) {
User u;
u.SetSecret(42);
assert(GetSecret(u) == 42); // XXX: why it works without detail:: ???
// DoNothing(); // this would indeed give a compile error
return 0;
}
然而,看起来像是
detail::GetSecret
在定义它的命名空间detail
之外被“逃脱”了,因此上面的程序可以编译并且断言通过。我很困惑,特别是在阅读ISO/IEC 14882:2011的7.3.1.2/3后:
每个在命名空间中首次声明的名称都是该命名空间的成员。如果非局部类中的友元声明首先声明一个类或函数,则友元类或函数是最内层封闭命名空间的成员。未限定查找(3.4.1)或限定查找(3.4.3)直到该命名空间范围内提供了匹配的声明时,才能找到友元的名称。如果调用友元函数,则其名称可能由将函数参数的类型关联到命名空间和类中的函数考虑名称查找(3.4.2)来找到。如果友元声明中的名称既不是限定的也不是模板ID,并且声明是函数或扩展类型说明符,则查找以确定实体是否先前已声明的范围不得考虑最内层封闭命名空间之外的任何作用域。
我使用了三个主要编译器(GCC、CLANG 和 ICC)的最新版本进行了检查,行为似乎是一致的。我在这里缺少什么,如何实现我最初想要的结果?