C++中const_cast的行为

7

这是我的问题,问题在注释中

const int a = 5;
const_cast<int&>(a)=7; //throw over const attribute in a,and assign to 7
std::cout<<a<<std::endl; //why still out put 5!!!!!!!!!!

有谁能告诉我为什么?还有一些书籍解释了这些问题并进行了推荐。谢谢!


6
这是未定义行为,它可能会随意执行。查看编译器输出的代码,它可能只是std::cout << 5 << std::endl; - chris
2
你向编译器撒谎,告诉它const是非常量。编译器有许多手段惩罚不诚实的程序员;-)(编译器反击的官方名称是“未定义行为”)。 - Sergey Kalinichenko
但在《Effective STL》中,作者使用了类似这样的代码:se是某个实现中的set,其中set的键为const,Emp::iterator i = se.find(...);... const_cast<Emp&>(*i).setValue(someValue);为什么它可以工作? - Ryan_Liu
@Ryan_Liu,你能否在你上面的问题底部粘贴Effective STL示例的完整代码?没有完整的上下文很难告诉你为什么它(可能)是有效的... - Jeff
1
@Ryan_Liu 对于Effective STL问题,如果你在某些你知道实际上并不是const的东西上进行了const_casting(比如容器节点的内容),那么这不是未定义行为。 - Andre Kostur
这让我想起了我一段时间前问过的一个问题:http://stackoverflow.com/questions/16668656/explanation-of-the-ub-while-changing-data - PaperBirdMaster
3个回答

12

就你目前的写法而言,这是未定义行为。如果你想以明确定义的方式查看 const_cast<> 的效果:

int a = 5;                  // note: not const. regular object.
const int& cref = a;        // const-reference to same object.
cref = 7;                   // illegal. cref is a const reference.
const_cast<int&>(cref) = 7; // legal. the original object a is not const.
由于原始变量a的非常量性质,这才是定义行为的唯一原因。你不能取出一个明确被限制为常量的对象并简单地强制转换掉其常量属性,这就是你发布的代码所做的事情(至少在我多次解释的时候是这样)。

1
据说,当编译器遇到像这样的常量值时:const int a;编译器有时会将该值存储在ROM中,程序的整个生命周期内都无法修改。因此,在创建时声明为const的值上强制取消const是未定义的行为。在稍后的某个时间点将非const值称为const,允许编译器强制执行对于程序的一部分值不会改变的限制。这些值可以以一种定义良好的方式取消其const属性,但通常这种转换是设计不良的标志。 - YoungJohn

4

C++标准草案7.1.6.1The cv-qualifiers4段指出:

[...]在其生命周期(3.8)内尝试修改const对象会导致未定义行为

因此,任何行为都是可能的,但您不应该这样做,绝不能依赖于这种行为。当然,正如const_cast是否安全?中接受的答案所说,const_cast确实有有效的用途:

只有在转换最初非常量变量时,const_cast才是安全的[...]

我们可以从这个 实例 看到你所看到的结果可能发生的一种情况,gcc 4.8.1 没有进行任何优化,只使用值 5 而不是读取当前值:
movl    $7, (%rax)
movl    $5, %esi
movl    $_ZSt4cout, %edi

在非常量情况下,我们会看到类似于以下内容:
movl    $7, -4(%rbp)
movl    -4(%rbp), %eax
movl    %eax, %esi
movl    $_ZSt4cout, %edi

2
编译器正在欺骗你,因为这个(编辑:你上面的用法确切地表示了这一点。)是明确定义的行为。当它看到在与 cout 语句相同作用域内定义的 a 常量时,很可能根本不会去查看内存。
你可以通过制造一个更矫揉造作的例子来愚弄编译器,但是以下修改至少在 gcc 中给出了你想要的结果:
volatile const int a = 5;

为了澄清,不是所有的const_cast用法都是未定义行为,请参考WhozCraig的示例。

但在<effective STL>中,作者使用了类似这样的代码:se是一个实现中的集合,其中set的键是const类型,Emp::iterator i = se.find(...);... const_cast<Emp&>(*i).setValue();为什么它能够工作? - Ryan_Liu
1
编译器欺骗了程序员?我会说情况恰恰相反。 ;) - Jim Buck
1
@Ryan_Liu 有一些编写const方法的常见模式(即不会修改非可变成员的方法),通过明智地使用const_cast避免与非const版本的代码重复。这是否是正在发生的事情? - Jeff
@Jim 不,伙计,程序员总是正确的,对吧? :) - Jeff
@Shafik 不错的观点,但我一直认为他试图故意混淆未定义的行为,这可能是一个错误。他更有可能误解了他在其他地方阅读的示例结构与他上面的代码等效。 - Jeff
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接