在回复我对某些answer in another question的评论时,有人提出类似
的内容void C::f() const
{
const_cast<C *>( this )->m_x = 1;
}
由于修改了const对象,会调用未定义的行为。这是真的?如果不是,请引用C ++标准(请提及您引用的标准),以便允许这样做。
对于它的价值,我总是使用这种方法来避免在只有一两个方法需要写入的情况下创建成员变量mutable
(因为使用mutable
使其可写入< em>所有方法)。
答案 0 :(得分:11)
(尝试)修改const对象(C ++ 11中的7.1.6.1/4)是未定义的行为。
所以重要的问题是,什么是const对象,而m_x
是一个?如果是,那么你有UB。如果不是,则此处没有任何内容表明它将是UB - 当然,由于此处未指出的其他原因(例如,数据竞争),它可能是UB。
如果在类f
的const实例上调用函数C
,则m_x
是一个const对象,因此行为未定义( 7.1.6.1/5):
const C c;
c.f(); // UB
如果在类f
的非const实例上调用函数C
,则m_x
不是const对象,因此就我们所知,行为已定义:
C c;
const C *ptr = &c;
c->f(); // OK
所以,如果你编写这个函数,那么你的用户不能创建C
的const实例并在其上调用函数。也许C
的实例只能由某个工厂创建,在这种情况下,您可以阻止它。
如果您希望数据成员即使整个对象为const
也可以修改,那么您应该将其标记为mutable
。这就是mutable
的用途,即使在f
的const实例上调用C
,它也会为您提供定义的行为。
从C ++ 11开始,const
数据成员上的mutable
成员函数和操作应该是线程安全的。否则,当您的类型与标准库函数和容器一起使用时,您违反了标准库提供的保证。
因此,在C ++ 11中,您需要使m_x
成为原子类型,或者以其他方式同步修改,或者作为最后的文档,即使它被标记为const,函数{ {1}}不是线程安全的。如果你没有做任何这些事情,那么你再次为用户创造了一个机会来编写他们合理认为应该工作但实际上有UB的代码。
答案 1 :(得分:8)
有两条规则:
您无法修改const对象。
您无法通过const指针或引用修改对象。
如果底层对象不是const,则不破坏任何规则。有一个共同的misunderstanding,对象的const指针或const引用的存在以某种方式阻止该对象改变或被改变。这只是一种误解。例如:
#include <iostream>
using namespace std;
// 'const' means *you* can't change the value through that reference
// It does not mean the value cannot change
void f(const int& x, int* y)
{
cout << "x = " << x << endl;
*y = 5;
cout << "x = " << x << endl;
}
int main()
{
int x = 10;
f(x, &x);
}
请注意没有演员表,没什么好笑的。然而,函数具有const引用的对象由该函数修改。这是允许的。你的代码是一样的,它只是通过抛弃constness来实现的。
但是,如果底层对象是const,则这是非法的。例如,我机器上的this code段错误:
#include <iostream>
using namespace std;
const int i = 5;
void cast(const int *j)
{
*const_cast<int *>(j) = 1;
}
int main(void)
{
cout << "i = " << i << endl;
cast(&i);
cout << "i = " << i << endl;
}
参见3.4.3(CV限定符)和5.2.7(丢弃常量)。
答案 2 :(得分:-1)
不再进一步搜索,C ++ 11标准中的第1.9 / 4节读取:
本国际标准中描述了某些其他操作 as undefined(例如,尝试修改const的效果) 对象)。
这就是你要在这里做的事情。你抛弃constness并不重要(如果你没有这样做,行为很明确:你的代码将无法编译)。您正在尝试修改const
对象,因此您遇到了未定义的行为。
您的代码在许多情况下似乎都有效。但是,如果您调用它的对象确实是const
并且运行时决定将其存储在只读内存中,则不会。除非您确定此对象最初不是const
,否则抛弃常量是危险的。