Related to my former question: Are compilers not allowed to assume const-ref parameters will stay const?
My new question is: Is there compiler-specific, non-standard ext
If i
is supposed to stay const for the whole function, and f()
has no side effects, you could declare it with __attribute__((pure))
:
int f(const int&) __attribute__((pure));
Note that it doesn't make sense for a pure
function to return void
, so I changed it to int
.
While this does not affect how f()
is compiled, it does affect the functions calling it (check it on godbolt):
#include <iostream>
int f(const int& i) __attribute__((pure));
int main() {
int i = 40;
f(i);
if (i != 40) {
std::cout << "not 40" << std::endl;
}
}
Here __attribute__((pure))
tells the compiler that f()
will not change i
, so the compiler will not generate the call to std::cout << ...
.
Without __attribute__((pure))
, even if f()
is declared to take a const int& i
parameter, the compiler has to assume that the value of i
may change, and generate the if
and the call to std::cout << ...
.
I do not know of non-standard ways to permit the compiler to assume immutability, but if I read the standard correctly, I think there is a standard way to do so (adapted from your other question):
void f(const int&);
struct ImmutableInt {
// it is UB to change val, because it is itself const
// not just a const reference
const int val;
};
void g3(const ImmutableInt & i) {
if (i.val == 42) f(i.val);
if (i.val == 42) f(i.val); // the compiler could assume i.val has not changed
}
The problem is, currently the compilers I checked do not make use of this knowledge to actually omit reloading the value.
I do not think there are any fundamental problems with this, because clang uses similar and more complicated reasoning about the virtual table pointer to achieve devirtualization. This reasoning is somewhat more complicated because the vptr will change during construction and is therefore not entirely const. The llvm metadata used for this is called invariant.group, but I do not know if you can set it yourself in C++.