何时可以使用 const_cast 删除 const 时修改值

When is it ok to modify a value when you remove const with const_cast?

本文关键字:const 修改 删除 可以使 何时 cast      更新时间:2023-10-16

根据 §7.1.5.1/4:

除了可以修改任何声明为可变 (7.1.1( 的类成员之外,任何在其生存期 (3.8( 期间修改 const 对象的尝试都会导致未定义的行为。

所以我的问题变成了:一个对象什么时候是常量对象?

特别是,非常量对象中的常量成员是否被视为常量对象?

class Foo {
    const Bar bar;
    void replaceBar(Bar bar2) {
        *(const_cast<Bar *>&bar) = bar2;  // Undefined behavior?
    }
}

出现这是因为我有一个不可变的类(所有字段都是 const(,但我想有一个移动构造函数,它在技术上修改传入的值。 在这种情况下,我可以接受"作弊",因为它不会破坏逻辑不变性。

简单的规则是:如果原始对象不是常量,则可以抛弃恒常性。因此,如果你有一个非cont对象,比如说,你将对它的const引用传递给一个函数,那么抛弃函数中的常量是合法的。在你的例子中,原始对象是恒量,所以抛弃恒常性是未定义的行为。

让我们做一个完整的例子:

struct Bar { int x; };
struct Foo {
  const Bar bar;
  Foo( int x ):bar(x) {}
  void replaceBar(Bar bar2) {
    *(const_cast<Bar *>&bar) = bar2;  // Undefined behavior?
  }
};

现在,让我们打破世界。

int main() {
  Foo f(3);
  Bar b = {2};
  f.replaceBar(b);
  std::cout << f.bar.x << "n";
}

以上可以而且可能应该输出 3,因为const对象Bar是用 x=3 创建的。 编译器可以并且应该假定const对象在其生存期内保持不变。

让我们打破更多世界:

struct Bar {
  int* x;
  Bar(int * p):x(p) {}
  ~Bar(){ if (x) delete x; }
  Bar(Bar&& o):x(o.x){o.x=nullptr;}
  Bar& operator=(Bar&& o){
    if (x) delete x;
    x = o.x;
    o.x = nullptr;
  }
  Bar(Bar const&)=delete;
  Bar& operator=(Bar const&)=delete;
};
struct Foo {
  const Bar bar;
  Foo( int* x ):bar(x) {}
  void replaceBar(Bar bar2) {
    *(const_cast<Bar *>&bar) = bar2;  // Undefined behavior?
  }
};

现在,同一个游戏可能会导致编译器删除某些内容两次。

int main() {
  int* p1 = new int(3);
  Foo f( p1 );
  Bar b( new int(2) );
  f.replaceBar(std::move(b));
}

并且编译器会在replaceBar内删除一次p1,并且应该在main结束时删除它。 它可以做到这一点,因为您保证f.bar.x在其范围结束之前保持不变(const(,那么您在 replaceBar 中违反了该承诺。

现在,这只是编译器有理由做的事情:一旦你修改了声明为const的对象,编译器就可以做任何事情,因为你已经调用了未定义的行为。 鼻魔,时间旅行 - 任何东西都可以争夺。

编译器使用某些行为未定义(即不允许(的事实进行优化。