首页 文章

引用类型的数据成员提供围绕const正确性的“漏洞”

提问于
浏览
19

我最近偶然发现了以下关于const-correctness的“漏洞”:

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 回答

  • -1

    Any modificationsconst 对象是未定义的行为,并且片段确实这样做 .

    该程序不是格式错误(这将需要编译错误),因为在初始化 inner 时,cv-qualifiers尚未生效 .

    从编译器的角度来看,要发出警告,需要它分析导致 inner.Modify() 的所有代码路径,并证明 inner 必须引用 const 对象,这在一般情况下是不可能的 .

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

  • 5

    这不是代码中的错误,请记住const指的是声明符的最内层元素 . 基本上ConstMethod上的const作出参考:

    Inner& const inner;
    

    当然,这并没有任何真正的区别,因为引用不能重新绑定 . 考虑用指针做同样的事情,你会意识到内部仍然可以被修改 . 如果是:

    Inner * const inner;
    

    你可以调用inner-> Modify() .

  • 1

    这不应该是未定义的行为 . 是的, myclass.outerMyClass::ConstMethod() const 内被视为 const ,但它没有以 const 开头存在,因此它应该通过非 const 引用安全地修改 . 当然,这样做可能会使您的代码的读者和/或用户感到惊讶,因此您应该尽量避免这种情况 .

    这类似于

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

    其中 constXPtr 的存在不会阻止人们通过 xPtr 修改 x .

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

相关问题