【发布时间】:2015-04-26 20:17:16
【问题描述】:
我想限制类字段 s.t. 的访问(或突变)。它只能通过专用函数/方法从给定的命名空间访问(或设置)。
由于友谊不是传递或继承的,我的想法是使用一个内部命名空间(应该是实现细节的那个)并将相应的功能作为朋友。例如,如果我们想存储一个秘密,并且只允许内部命名空间内的代码访问它并允许其他人设置它,我希望这样可以解决问题:
#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 在它被定义的地方,所以上面的程序编译并且断言通过了。我很困惑,尤其是在我阅读了 7.3.1.2/3(ISO/IEC 14882:2011)之后:
在命名空间中首先声明的每个名称都是该命名空间的成员 命名空间。如果非本地类中的友元声明首先声明 类或函数友元类或函数是 最内层的封闭命名空间。找不到朋友的名字 不合格查找 (3.4.1) 或合格查找 (3.4.3) 直到 在该命名空间范围内提供了匹配的声明( 在授予友谊的类定义之前或之后)。如果有朋友 函数被调用,它的名字可以通过名字查找找到 考虑来自与 函数参数的类型(3.4.2)。如果在朋友的名字 声明既不是限定的,也不是模板 ID 和声明 是一个函数或一个详细的类型说明符,查找来确定 该实体是否先前已申报,不应考虑任何 最里面的封闭命名空间之外的范围。
我检查了三个主要编译器(GCC、CLANG 和 ICC)的最新版本,其行为似乎是一致的。我在这里缺少什么以及如何实现我最初的意图?
【问题讨论】:
-
你想找到一种方法来禁用 argument-dependent lookup,我想。
标签: c++ oop namespaces encapsulation friend