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

时间:2018-07-20 18:11:44

标签: c++ class reference constants

我最近偶然发现以下有关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样式强制类型转换的程序中调用了与const正确性相关的未定义行为。

所以,我的问题是:

  • 我是否正确地说上述程序具有不确定的行为?
  • 如果是,这是语言错误吗?上面的程序中是否有一行可以不应该(可以合理地使之不)编译?
  • 在实践中是否应遵循一些准则来避免这类未定义的行为?

3 个答案:

答案 0 :(得分:5)

Any modificationsconst对象的行为是不确定的,摘录确实可以做到这一点。

该程序不是格式错误的(这将需要编译错误),因为在初始化inner时,cv限定词尚未生效。

从编译器的角度来看,要发出警告,将要求它分析直至inner.Modify()的所有代码路径,并证明inner 必须指的是{{ 1}}对象,在一般情况下是不可能的。

最好的建议可能是没有内部指针/引用,反正还是邪恶的。

答案 1 :(得分:1)

这不是代码中的错误,请记住const指向声明符的最内层元素。基本上,ConstMethod上的const可以引用:

Inner& const inner;

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

Inner * const inner;

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

答案 2 :(得分:-1)

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

这类似于

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

其中constXPtr的存在不会(也不应该)阻止人们通过x修改xPtr

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