The code in the above example translates into the following assembler:
movl $10, 28(%esp) //const int i = 10;
leal 28(%esp), %eax //int* wp = const_cast <int*>(&i);
movl %eax, 24(%esp) //store the pointer on the stack
movl 24(%esp), %eax //place the value of wp in eax
movl $20, (%eax) //*wp = 20; - so all good until here
movl $10, 4(%esp) //place constant value 10 onto the the stack for use in printf
movl $.LC0, (%esp) // load string
call printf //call printf
Because the original int i was declared constant, the compiler reserves the right to use the literal value instead of the value stored on the stack. This means that the value does not get changed and you are stuck with the original 10.
The moral of the story is compile time constants should remain constant because that is what you are telling the compiler. The moral of the story is that casting away constness in order to change a constant can lead to bad things.