代码之家  ›  专栏  ›  技术社区  ›  HighCommander4

引用类型的数据成员提供了有关常量正确性的“漏洞”

  •  19
  • HighCommander4  · 技术社区  · 8 年前

    我最近偶然发现了以下关于常量正确性的“漏洞”:

    struct Inner {
      int field = 0;
      void Modify() {
        field++;
      }
    };
    
    struct Outer {
      Inner inner;
    };
    
    class MyClass {
    public:
      Outer outer;
      Inner& inner; // refers to outer.inner, for convenience
    
      MyClass() : inner(outer.inner) {}
    
      void ConstMethod() const {
        inner.Modify();  // oops; compiles
      }
    };
    

    此外,似乎还可以使用此漏洞修改声明为 const ,我认为这是未定义的行为:

    int main() {
        const MyClass myclass;
        std::cout << myclass.outer.inner.field << "\n";  // prints 0
        myclass.ConstMethod();
        std::cout << myclass.outer.inner.field << "\n";  // prints 1
    }
    

    这让我感到害怕,因为我似乎刚刚在一个不使用的程序中调用了与常量正确性相关的未定义行为。 const_cast 或者使用C样式的演员表来抛弃Constness。

    所以,我的问题是:

    • 我说上面的程序有未定义的行为是正确的吗?
    • 如果是,这是一个语言错误吗?上面的程序中是否有一行可以证明 不应该 (可以合理地不)编译?
    • 在实践中,是否有一些应该遵循的准则来避免这类未定义的行为?
    3 回复  |  直到 8 年前
        1
  •  5
  •   Passer By    8 年前

    Any modifications 到A const 对象是未定义的行为,而代码片段确实做到了这一点。

    程序没有格式错误(这需要编译错误),因为在初始化时 inner ,cv限定符尚未生效。

    从编译器的角度来看,发出警告需要它分析所有的代码路径 inner.Modify() 并证明 内部的 必须 指的是 康斯特 对象,在一般情况下是不可能的。

    最好的建议可能没有内部指针/引用,这无论如何都是邪恶的。

        2
  •  1
  •   SoronelHaetir    8 年前

    这不是代码中的错误,请记住const引用声明器的最里面的元素。const on constmethod基本上参考了:

    Inner& const inner;
    

    当然,这不会有任何实际的区别,因为引用不能被重新绑定。想想用指针做同样的事情,你会发现内部仍然可以被修改。如果是:

    Inner * const inner;
    

    您可以调用inner->modify()。

        3
  •  -1
  •   Joshua Green    8 年前

    这不应该是未定义的行为。对, myclass.outer 被视为 const 里面 MyClass::ConstMethod() const 但是没有 开始 它的存在是 康斯特 因此,它应该可以通过- 康斯特 参考文献。当然,这样做可能会使代码的读者和/或用户感到惊讶,所以您应该尽量避免这样做。

    这类似于

    int x = 5;
    int * xPtr = &x;
    const int * constXPtr = &x;
    

    其中的存在 constXPtr 不能(也不应该)阻止一个人修改 x 通过 xPtr .

    值得注意的是,这种别名的可能性会阻止许多潜在的编译器优化。