The key difference between `const` and the `register` and `inline` keywords is that, despite all of them often flagging optimisations that the compiler can often work out anyway, `const` still aids human comprehension by declaring constraints, whereas the latter two do not.
It seems that interprocedure optimisations in modern compilers make a lot of const-by-reference optimisations apply even when the code is mutable-by-reference in the parameter list but the function body doesn't modify it in practice. This would only work if it could deterministically work out which function is called.
Local constant stack values surely can be completely deterministically verified as such by the optimiser even without the `const` modifier. They could be overwritten without a C assignment to it, via the stack from a buffer overflow of an array next to it, but that's undefined behaviour so a compiler is presumably free to assume it is not modified, eliminate unnecessary register/memory loading code, and let the developers deal with the consequences.
As trailing `const` on member functions outlaws modifications via `this`, it would follow that the same optimisation-even-without-modifier process would apply as to `const` local stack values.
As constancy is a constraint that aids human comprehension, there's a good reason for choosing a keyword just as short as the mutable equivalent, such as Swift's `let` vs `var`; if the more constrained equivalent is equally or more convenient, more constrained and thus easy-to-reason-about code becomes more common.
While specifying inline alone is useless for optimization, the inline specifier is important for another type of optimization: putting a function in a header file. If you put a function in a header file and don't mark it inline, that tends to be undefined behavior because it tends to violate the One Definition Rule, (both C and C++).
You typically dont hit undefined behavior (maybe technically undefined by the standard), bit you typically get hit with really annoying linker errors about duplicately defined symbols. And, yeah, annoying because of cryptic error messages.
Ive usually encoutered this most when trying to implement out of line template member functions and forget to include the inline keyword.
Force inlining code is actually quite a useful optimisation still. We have found many instances that after a few inline functions the compiler gives up and just calls the function instead. Force inlining certain low level functions has actually improved performance by several percent in our codebase.
From the language standpoint, the inline specifier doesn't force the compiler to inline the function. But I guess some compilers could provide guarantees that go beyond what the spec says.
I was talking about __forceinline for MSVC and things like it for other compilers. One's which for better or worse will make your function inlined.
One thing that I did find out what that even if you inline or force inline a function, that if you have it exported from a DLL or in a class that is exported as a DLL, then the function might still be called non-inline.
You’re completely correct. I was conflating the ODR-adhering standard ‘inline’ keyword with the various inlining pragma in some compilers that actually are about solely forcing inlining as an optimisation.
How does making the function static not solve this in C? Then the definition is local to the translation unit and there are no linkage concerns at all.
> Static but not inline functions will trigger warnings of unused functions.
Well, isn't it true? It's true also in the inline case. I consider it strange behavior not to emit an explicitly enabled warning for a case that definitely satisfies the warning conditions.
> Static and inline does not trigger this.
Tried -Wunused-function in gcc 6.3.0 and clang 3.8.1-24, with an unused-but-defined function declared "static inline int add(int a, int b)". clang still emits the warning, gcc does not.
If you're already dependent on whatever additional semantics gcc tacked onto the inline keyword you can as well use "__attribute__((unused))" to disable the warning for a particular function, which more clearly communicates the intent to disable the warning.
It turns out that the clang warnings work differently in header files, specifically. In my test I just put the static inline function in a C file. clang does however not trigger -Wunused-function when the static inline function is included from a header. The code is the same after pre-processor expansion.
IMO these special cases just add to the confusion and my criticism of strange behavior, but I stand corrected on the actual behavior of the two compilers.
By ‘constant stack value’, I was referring to ‘effectively constant’ stack values, i.e. ones who aren’t reassigned in the scope or passed by mutable reference (or indeed are but aren’t actually modified by those functions anyway if the compiler can prove it).
The compiler will detect any attempt at taking the address of the variable (including fancy things like most inline assembly) and skip the optimization, marking it as requiring a memory location.
However, it can still cheat by rearranging the memory write or even letting linker initialize the address.
It seems that interprocedure optimisations in modern compilers make a lot of const-by-reference optimisations apply even when the code is mutable-by-reference in the parameter list but the function body doesn't modify it in practice. This would only work if it could deterministically work out which function is called.
Local constant stack values surely can be completely deterministically verified as such by the optimiser even without the `const` modifier. They could be overwritten without a C assignment to it, via the stack from a buffer overflow of an array next to it, but that's undefined behaviour so a compiler is presumably free to assume it is not modified, eliminate unnecessary register/memory loading code, and let the developers deal with the consequences.
As trailing `const` on member functions outlaws modifications via `this`, it would follow that the same optimisation-even-without-modifier process would apply as to `const` local stack values.
As constancy is a constraint that aids human comprehension, there's a good reason for choosing a keyword just as short as the mutable equivalent, such as Swift's `let` vs `var`; if the more constrained equivalent is equally or more convenient, more constrained and thus easy-to-reason-about code becomes more common.