Leveldb 编译错误背后的C++标准变化
在编译 Levedb 时,我遇到了这个错误:
g++ -c -I. -I./include -fno-builtin-memcmp -DLEVELDB_PLATFORM_POSIX -pthread -DOS_LINUX -O2 -DNDEBUG db/version_set.cc -o db/version_set.o
db/version_set.cc: In member function `void leveldb::VersionSet::Builder::Apply(leveldb::VersionEdit*)\':
./db/version_edit.h:100: error: `std::vector leveldb::VersionEdit::compact_pointers_\' is private
db/version_set.cc:461: error: within this context
...
在网上容易搜到解决方案,由于归根结底是访问控制问题,方法是把所有涉及到的的 private 变量或类型修改为 public。由于不是所有的编译器都会报错,我就很好奇产生这个错误的根本原因。
BTW: 一种不修改代码的 work around 方法是,在编译这个文件时加上 -fno-access-control 参数,这样 g++ 就不会进行访问控制检查,自然也就没问题了。这个参数同样可以用于对 private 成员函数进行单元测试。
简单地分析一下这个错误。发生错误的地方是在 VersionSet::Builder 这个类的成员函数中,而错误则是其成员函数无法访问 VersionEdit 和 Version 类的私有成员变量。VersionSet 是 VersionEdit 和 Version 类的友元类,Builder 是 VersionSet 的嵌套类。简化一下,代码如下所示:
class VersionSet;
class VersionEdit {
friend class VersionSet;
static int compact_pointers_;
};
class VersionSet {
class Builder {
int foo()
{
return VersionEdit::compact_pointers_;
}
};
};
把这段代码拿给编译器去编译,g++ 3.4.4/5 会报类似的 `int VersionEdit::compact_pointers_\' is private 错误,但是 g++ 4.5.3 则能够编译通过。
由于 VersionSet 是 VersionEdit 的友元类,那么 VersionSet 是能够访问 VersionEdit 私有成员的,这样问题就集中在 Builder 是否能够获得与 VersionEdit 的友元关系。如果语法规定嵌套类 Builder 能够从 VersionSet “获得”友元关系,那么 Builder就能够访问 VersionEdit::compact_pointers_,反之就不能访问。
在 C++98 标准中,关于嵌套类的权限有如下描述:
$11.8/1 [class.access.nest],
The members of a nested class have no special access to members of an enclosing class, nor to classes or functions that have granted friendship to an enclosing class; the usual access rules (clause 11) shall be obeyed. The members of an enclosing class have no special access to members of a nested class; the usual access rules (clause 11) shall be obeyed.
Example:
class E {
int x;
class B { };
class I {
B b; // error: E::B is private
int y;
void f(E* p, int i) {
p->x = i; // error: E::x is private
}
};
int g(I* p)
{
return p->y; // error: I::y is private
}
};
但是在 C++11 中,这段描述变更为:
$11.7/1 Nested classes [class.access.nest]
A nested class is a member and as such has the same access rights as any other member. The members of an enclosing class have no special access to members of a nested class; the usual access rules (Clause 11) shall be obeyed.
Example:
class E {
int x;
class B { };
class I {
B b; // OK: E::I can access E::B
int y;
void f(E* p, int i) {
p->x = i; // OK: E::I can access E::x
}
};
int g(I* p) {
return p->y; // error: I::y is private
}
};
从上面的描述和示例代码对比中我们可以明显看出,在旧标准中嵌套类和“被嵌套类”没有什么特殊的关系,就像两个普通类一样;但是在新标准中嵌套类已经完全视为“被嵌套类”的成员,那么自然也获得了“被嵌套类”成员应该有的访问控制权限。这也就意味着“被嵌套类”的普通成员拥有的访问“被友元类”私有成员变量的权限,嵌套类也能够获得,那么 Leveldb 在新版本的编译器下能够编译通过也不足为奇了。
不过 gcc3.4 的编译错误问题还不能单单归究于标准的变化。因为 gcc3.4 已经能够支持嵌套类访问“被嵌套类”的私有成员(因为在很早以前这就被确认为一个缺陷),只是不能够支持友元关系到嵌套类的传递。友元关系的传递可能是在 4.1 或者 4.2 版本中实现的,应该属于上述标准变化的衍生特性。
建议继续学习:
扫一扫订阅我的微信号:IT技术博客大学习
- 作者:Solrex Yang 来源: Solrex Shuffling
- 标签: Leveldb
- 发布时间:2011-09-25 13:26:22
- [44] 如何拿下简短的域名
- [44] IOS安全–浅谈关于IOS加固的几种方法
- [43] Oracle MTS模式下 进程地址与会话信
- [43] 图书馆的世界纪录
- [41] 界面设计速成
- [41] android 开发入门
- [40] 【社会化设计】自我(self)部分――欢迎区
- [38] 读书笔记-壹百度:百度十年千倍的29条法则
- [37] 视觉调整-设计师 vs. 逻辑
- [34] 程序员技术练级攻略