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

时间:2014-12-04 14:54:05

标签: c++ const-cast

根据§7.1.5.1/ 4:

  

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

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

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

class Foo {
    const Bar bar;

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

这是因为我有一个不可变类(所有字段都是const),但是我想要一个移动构造函数,它在技术上修改了传入的值。在这种情况下,我很好“作弊”,因为它不破坏逻辑常数。

2 个答案:

答案 0 :(得分:2)

简单的规则是:如果原始对象不是const,则可以抛弃constness。因此,如果你有一个非cont对象,并且你将const引用传递给一个函数,那么抛弃函数中的const是合法的。 在您的示例中,原始对象是const,因此将const constness抛弃为未定义的行为。

答案 1 :(得分:0)

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

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));
}

并且编译器将在p1内删除replaceBar一次,并且应该在main的末尾删除它。它可以执行此操作,因为您保证f.bar.x保持不变(const)直到其范围结束,然后您违反了replaceBar中的承诺。

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

编译器使用的事实是某些行为未定义(也就是说,不允许)进行优化。