请考虑以下代码:
void func1(const int &i);
void func2(int i);
void f()
{
int a=12;
func1(a);
func2(a);
}
使用带有-O3的g ++ 4.6编译,我可以看到编译后重新读取" a"的值。函数调用之间。将一个定义更改为" const int",编译器不会这样做,而是简单地加载立即值" 12"进入edi。如果a不是const,则同样如此,但我将func1的签名更改为按值接受。
虽然不是代码生成中的错误,但这仍然是奇怪的行为。因为func1承诺不改变a,为什么编译器的代码会根据a是否为const而改变?
编辑:一些怀疑论者声称我可能会错误地阅读代码。上面的代码使用-S编译产生以下内容:
_Z1fv:
.LFB0:
.cfi_startproc
subq $24, %rsp
.cfi_def_cfa_offset 32
leaq 12(%rsp), %rdi
movl $12, 12(%rsp)
call _Z5func1RKi
movl 12(%rsp), %edi <-- Rereading a
call _Z5func2i
addq $24, %rsp
.cfi_def_cfa_offset 8
ret
.cfi_endproc
将a更改为const会产生:
_Z1fv:
.LFB0:
.cfi_startproc
subq $24, %rsp
.cfi_def_cfa_offset 32
leaq 12(%rsp), %rdi
movl $12, 12(%rsp)
call _Z5func1RKi
movl $12, %edi <-- Use immediate value
call _Z5func2i
addq $24, %rsp
.cfi_def_cfa_offset 8
ret
.cfi_endproc
答案 0 :(得分:6)
在C ++中,const
实际上只是逻辑常量,而不是物理常量。 func1
可以执行const_cast
并修改i
。 const
就像枪的安全 - 你仍然可以用脚射击自己,但不是偶然的。
作为T.C.和juanchopanza在评论中指出,抛弃对象的const
并修改它是UB。引用&#34; Notes&#34; here:
尽管const_cast可以从任何指针或引用中删除constness或volatile,但是使用结果指针或引用来写入声明为const的对象或访问声明为volatile的对象会调用未定义的行为。
答案 1 :(得分:3)
总结答案,我认为这最能解释:
对非const变量采用const引用,然后抛弃constness是合法的。因此,第一种情况下的编译器不能假设func1不会更改a
。
未定义如果将constness转换为声明为const的变量会发生什么。第二种情况下的编译器可能假设func1不会抛弃constness。如果func1抛弃了常量,func2将收到&#34;错误&#34;价值,但这只是未定义行为的一个后果。