C++ reliance on argument to const reference not changing

215 Views Asked by At

Please consider the following code:

void func1(const int &i);
void func2(int i);

void f()
{
  int a=12;
  func1(a);
  func2(a);
}

Compiled with g++ 4.6 with -O3, I can see that the compiled re-reads the value of "a" between the function calls. Changing a's definition to "const int", the compiler doesn't do that, and instead simply loads immediate value "12" into edi. The same is true if a is not const, but I change func1's signature to accept by value.

While not a bug in the code generation, this is still weird behavior. Being as it is that func1 made a commitment not to change a, why should the compiler's code change based on whether a is const or not?

Edit: Some skeptics claim that I might be reading the code wrong. The above code produces the following with -S compilation:

_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                                                           

Changing a to const produces:

_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
2

There are 2 best solutions below

6
On BEST ANSWER

In C++, const is really just logical constness and not physical constness. func1 can do a const_cast and modify i. const is like the safety of a gun - you can still shoot yourself in the foot, but not by accident.

As T.C. and juanchopanza have pointed out in the comments, casting away the constness of an object and modifying it is UB. Quoting from "Notes" here :

Even though const_cast may remove constness or volatility from any pointer or reference, using the resulting pointer or reference to write to an object that was declared const or to access an object that was declared volatile invokes undefined behavior.

0
On

Summing up the answers, I think this explains it best:

It is legal to take a const reference to a non-const variable, and then cast away the constness. Therefore, the compiler in the first case cannot assume that func1 will not change a.

It is undefined what happens if you cast away the constness to a variable declared const. The compiler in the second case may assume that func1 will not cast away the constness. If func1 does cast away the constness, func2 will receive the "wrong" value, but that's just one consequence of undefined behaviour.