If you want a value to be a true constant, you can pass it as a template value argument.
template<int i>
void function(){
*side_effect = 123;
}
there is no way for any operation to modify i
.
This requires that the input be a compile-time constant (and verified so by the compiler at compile time).
So this doesn't work:
int main(){
int i = 0;
side_effect = &i;
function<i>();
}
as i
is not a compile-time constant. If we instead made it:
int main(){
const int i = 0;
side_effect = &i;
function<i>();
}
the function<i>
line works, but the side_effect = &i
doesn't work. If we add in a cast:
int main(){
const int i = 0;
side_effect = const_cast<int*>(&i);
function<i>();
}
now the operation *side_effect = 123
becomes UB within function
.
Another approach which doesn't require that the value being passed in is a true compile-time constant is to not take a reference:
void function(int i){
*side_effect = 123;
}
and instead take a local copy (either int
or const int
as the argument, depending on if we want function
to have the rights to modify i
).
The full strength version of what you want -- that we take a reference to external data, and then ensure that the external data remains unchanged -- can pretty easily be shown to be equivalent to the halting problem in the general case.