在回应我对另一个问题的一些答案的评论时,有人建议像
void C::f() const
{
const_cast<C *>( this )->m_x = 1;
}
由于修改了 const 对象,因此调用未定义的行为。这是真的?如果不是,请引用允许这样做的 C++ 标准(请说明您引用的标准)。
对于它的价值,我一直使用这种方法来避免创建一个成员变量mutable
,如果只有一个或两个方法需要写入它(因为 usingmutable
使它可写入所有方法)。
在回应我对另一个问题的一些答案的评论时,有人建议像
void C::f() const
{
const_cast<C *>( this )->m_x = 1;
}
由于修改了 const 对象,因此调用未定义的行为。这是真的?如果不是,请引用允许这样做的 C++ 标准(请说明您引用的标准)。
对于它的价值,我一直使用这种方法来避免创建一个成员变量mutable
,如果只有一个或两个方法需要写入它(因为 usingmutable
使它可写入所有方法)。
(尝试)修改 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
因此,如果您编写此函数,那么您将受用户支配,不要创建 const 实例C
并在其上调用该函数。也许实例C
仅由某个工厂创建,在这种情况下,您可以防止这种情况发生。
如果您希望一个数据成员即使是完整的对象也是可修改的const
,那么您应该标记它mutable
。这就是mutable
目的,它为您提供定义的行为,即使f
在C
.
从 C++11 开始,const
成员函数和对mutable
数据成员的操作应该是线程安全的。否则,当您的类型与标准库函数和容器一起使用时,您将违反标准库提供的保证。
因此,在 C++11 中,您需要创建m_x
一个原子类型,或者以其他方式同步修改,或者作为最后的手段文档,即使它被标记为 const,该函数f
也不是线程安全的。如果你不做任何这些事情,那么你再次为用户创造了一个机会来编写他们合理地认为应该工作但实际上有 UB 的代码。
有两个规则:
您不能修改 const 对象。
您不能通过 const 指针或引用来修改对象。
如果基础对象不是 const,则您不会违反任何规则。有一个常见的误解,即存在对对象的 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,则这是非法的。例如,此代码在我的机器上出现段错误:
#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 节(抛弃 constness)。
无需进一步搜索,C++11 标准中的第 1.9/4 节内容如下:
本国际标准中将某些其他操作描述为未定义的(例如,尝试修改 const 对象的效果)。
这就是你在这里想要做的。你抛弃 constness 并不重要(如果你不这样做,行为就很好定义了:你的代码将无法编译)。您正在尝试修改const
对象,因此遇到了未定义的行为。
您的代码似乎在许多情况下都可以工作。但是,如果您调用它的对象确实是const
并且运行时决定将其存储在只读内存中,则不会。const
除非你真的确定这个对象不是最初的,否则抛弃 constness 是危险的。