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, std::allocator > > 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 版本中实现的,应该属于上述标准变化的衍生特性。
您可能对这些感兴趣:
- 僵尸对象或 RAII (4) - 我最近在想这个问题,到底要不要在程序中使用异常?以前写的 C 代码比较多,即使写 C++,基本上也是...
- Math in CS:置换的轮换分解 (8) - 随便一本《近世代数》或者《抽象代数》书上在讲到置换群的时候,应该都会讲到这样一个定理:任何一个置换都...
- Cygwin GCC qsort 函数错误(续) (0) - 上一篇文章中提到我在为 qsort 写 compare 函数时犯了一个愚蠢的错误:我脑袋陷入了一个错...
- Cygwin GCC qsort 函数错误 (6) - 我平时在 Windows 下写代码时,经常使用 Cygwin 的 gcc。但是今天我居然发现 Cyg...
- 将文本文件读入数组-C语言实现 (8) - 要求:使用 C 语言将文本文件的每一行读入为数组的一个元素,返回一个 char ** 指针。由于行长...
- vasprintf 会将空间分配到栈上吗? (11) - 由于提交过几次 Linux Fetion 的 bug 和 patch,Linux Fetion 的开...