技术头条 - 一个快速在微博传播文章的方式     搜索本站
您现在的位置首页 --> 其他 --> Leveldb 编译错误背后的C++标准变化

Leveldb 编译错误背后的C++标准变化

浏览:2446次  出处信息

    在编译 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 版本中实现的,应该属于上述标准变化的衍生特性。

建议继续学习:

  1. leveldb性能分析和表现    (阅读:6951)
  2. leveldb 的实现    (阅读:3319)
  3. LevelDB 的原理和动机    (阅读:2352)
QQ技术交流群:445447336,欢迎加入!
扫一扫订阅我的微信号:IT技术博客大学习
© 2009 - 2024 by blogread.cn 微博:@IT技术博客大学习

京ICP备15002552号-1