是否从"this"中抛弃constness然后更改成员值会调用未定义的行为?

Fre*_*abe 6 c++ const standards-compliance const-cast

在回答我对另一个问题中某些答案的评论时,有人建议像

void C::f() const
{
  const_cast<C *>( this )->m_x = 1;
}
Run Code Online (Sandbox Code Playgroud)

因为修改了const对象,所以会调用未定义的行为.这是真的?如果不是,请引用C++标准(请提及您引用的标准),这样可以实现此目的.

对于它的价值,我总是使用这种方法来避免制作成员变量,mutable如果只需要一两个方法就可以写入它(因为使用mutable它可以写入所有方法).

Ste*_*sop 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
Run Code Online (Sandbox Code Playgroud)

如果f在类的非const实例上调用该函数C,那么m_x它不是const对象,因此我们知道行为是定义的:

C c;
const C *ptr = &c;
c->f(); // OK
Run Code Online (Sandbox Code Playgroud)

因此,如果您编写此函数,那么您将受到用户的支配而不是创建一个const实例C并在其上调用该函数.也许C只有某些工厂才能创建实例,在这种情况下,您可以防止这种情况发生.

如果您希望数据成员即使完整对象也可以修改const,那么您应该标记它mutable.这就是为什么mutable,即使f在const实例上调用它,它也会为你提供定义的行为C.

从C++ 11开始,数据成员的const成员函数和操作mutable应该是线程安全的.否则,当您的类型与标准库函数和容器一起使用时,您违反了标准库提供的保证.

因此,在C++ 11中,您需要创建m_x一个原子类型,或者以其他方式同步修改,或者作为最后的文档,即使它被标记为const,该函数f也不是线程安全的.如果你没有做任何这些事情,那么你再次创造了一个机会让用户编写他们合理认为应该工作但实际上有UB的代码.

  • +1.我想补充一点,提及`mutable`,正如名称所示,是允许变异,而`const_cast`是为了打破类型系统以使用传统的,非const正确的API. (5认同)

Dav*_*rtz 8

有两个规则:

  1. 您无法修改const对象.

  2. 您不能通过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);
}
Run Code Online (Sandbox Code Playgroud)

注意没有演员阵容,没什么好笑的.然而,函数具有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;
}
Run Code Online (Sandbox Code Playgroud)

参见3.4.3(CV限定符)和5.2.7(丢弃常数).

  • 也许我没有仔细阅读,但我没有看到问题说对象本身保证不是`const`,所以我假设它很可能是`const`,使得代码为UB. (3认同)