根据§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),但是我想要一个移动构造函数,它在技术上修改了传入的值。在这种情况下,我很好“作弊”,因为它不破坏逻辑常数。
答案 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
的对象,编译器就可以完成任何,因为你调用了未定义的行为。鼻子恶魔,时间旅行 - 任何东西都可以争夺。
编译器使用的事实是某些行为未定义(也就是说,不允许)进行优化。