C++ 引用类型的数据成员提供;“漏洞”;关于常量正确性
我最近偶然发现了以下关于常量正确性的“漏洞”:C++ 引用类型的数据成员提供;“漏洞”;关于常量正确性,c++,class,reference,constants,C++,Class,Reference,Constants,我最近偶然发现了以下关于常量正确性的“漏洞”: 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
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
}
intmain(){
const MyClass MyClass;
std::cout这不是代码中的错误,请记住const指的是声明器的最里面的元素。基本上,const on const方法进行引用:
Inner& const inner;
当然,这不会产生任何真正的区别,因为引用无法重新绑定。考虑使用指针执行相同的操作,您会发现内部仍然可以修改。如果是:
Inner * const inner;
您可以调用internal->Modify()。到const
对象是未定义的行为,而代码段确实做到了这一点
程序不是格式错误(这将需要编译错误),因为在初始化内部时,cv限定符尚未生效
从编译器的角度来看,发出警告需要它分析通向internal.Modify()
的所有代码路径,并证明internal
必须引用const
对象,这在一般情况下是不可能的
最好的建议是不要使用内部指针/引用,这无论如何都是有害的。这不应该是未定义的行为。是的,myclass.outer
被视为const
insidemyclass::ConstMethod()const
,但它并不是以const
的形式出现的,因此应该可以通过非const
引用安全地修改它。当然,这样做可能会让代码的读者和/或用户感到惊讶,所以您应该尽量避免这种情况
这类似于
int x = 5;
int * xPtr = &x;
const int * constXPtr = &x;
其中constXPtr
的存在不会(也不应该)阻止人们通过xPtr
修改x
值得注意的是,这种别名可能会阻止许多潜在的编译器优化。如果打开警告,您对此有何了解?我实际上并不认为这是未定义的,只是不可取。它基于一个简单的事实,即在构造函数和析构函数中,const
对象始终被视为非const对象。是的,您可以使用它来绕过常量正确性规则,而无需强制转换。这不是一个答案,而是有助于防止出现这种情况的东西:正如我在下面指出的,相关的问题是myclass.internal
是否是“首次创建的”作为const
。在MyClass
的定义中,它不是const
,所以我真的不知道!我知道它为什么要编译-这不是问题所在。问题是代码是否调用了未定义的行为,以及是否可以在编译时修改语言规则来捕获它。但是MyClass.inner
不是const
——它只是MyClass::ConstMethod()const
中的const
。该页面的关键引用(加上重点):“首次创建对象时,使用的cv限定符……确定对象的常量或波动性…”哎呀,错过了myclass
本身被声明为const
。但不知道如何修复我造成的损坏。对不起,myclass.outer
在“首次创建”时被认为是const
,现在还不完全清楚这就是相关的问题。@JoshuaGreen子对象显然是对象的一部分,否则你可以对一个类做任何你想做的事情。请阅读,如果你仍然不相信的话。Oops。从某种意义上说,上面的信息很有用,但我刚刚注意到OP将myclass
声明为const
。因此,这样做了es确实看起来像是类型系统中的一个不幸的漏洞,创建了一个const
对象,同时将一个非const
引用绑定到它。现在还不完全清楚myclass.outer
在“首次创建”时是否被认为是const
,这是相关的问题。