I think probably
not, because
determinant() and
inverse() are two completely separate function calls. This would require the compiler to “save” the result of
determinant() in the object instance, so that it can be re-used inside
inverse() later on – which would change the memory layout of the object. Maybe it could happen if both, the call to
determinant() and
inverse(), get completely “inlined” at the place where they are called, so that the result of
determinant() can be carried over directly in a temporary (e.g. register). But I think it is unlikely that the compiler is “smart” enough to do this. Anyhow, you won't know for sure until you look at the generated assembly code!
https://learn.microsoft.com/en-us/cpp/build/reference/fa-fa-listing-file?view=msvc-170
https://gcc.gnu.org/onlinedocs/gcc/Overall-Options.html#index-S
BTW: I think explicitly caching the determinant, as others have suggested, is the way to go here. Make
determinant() “lazily” compute the value (and store it in the cache), if it hasn't been computed yet; otherwise just return the cached value. Using
mutable for this kind of “transparent” cache makes perfect sense to me, because even if you have a
const reference to the object and therefore you can
not change the actual state of the object, a “read-only” operation, such as
determinant(), may still need to update the cache.
Using
mutable for an internal “cache” variable does
not break thread safety – provided that the “cache” variable is
not static, and provided that each thread uses its own separate object instance. Meanwhile, sharing the
same object instance between concurrent threads (without a proper explicit synchronization) is
never “safe”, unless the class' documentation explicitly says so!
Even if we ignore that
mutable fields may exist: Having a
const reference to a “shared” object does
not ensure that the object cannot be modified through other
non-const references elsewhere. So, even if thread #1 “reads” an object via a
const reference, thread #2 may still modify that same object at the same time via a
non-const reference, resulting (potentially) in an inconsistent read result.
Sharing an object instance between concurrent threads (without explicit synchronization) is “safe”
only if either: (1) the object is truly
immutable, or (2) the class internally/implicitly
synchronizes all access (read and write) to the mutable state. Here it is important to note that, in C++, just declaring a reference as
const does
not magically make the underlying object immutable!
(This is different from, e.g., Rust, where the compiler
enforces that there are either
n non-mutable references or exactly
one mutable reference to an object, but never both at the same time; so if you have a non-mutable reference to an object, you can be sure nonbody else is modifying the object, because nobody else can have a mutable reference while you are holding the non-mutable reference)