The compiler can do a lot more than you may think.
I'm not sure what you were going for in your example, but consider the following piece of code:
template <typename TLazyChar>
struct lazyUpperImpl{
TLazyChar in;
lazyUpperImpl(TLazyChar in_):in(in_){}
char constexpr operator()(){
auto c = in();
if (c >= 'a' && c <= 'z'){
c = c - 'a' + 'A';
}
return c;
}
};
template <typename TLazyNumeric>
struct lazyAdd5Impl{
TLazyNumeric in;
lazyAdd5Impl(TLazyNumeric in_):in(in_){}
int constexpr operator()(){
return in() + 5;
}
};
template <typename Tout, typename TLazyIn>
struct lazyCastImpl {
TLazyIn in;
lazyCastImpl(TLazyIn in_):in(in_){}
Tout constexpr operator()(){
return static_cast<Tout>(in());
}
};
template <typename Tout, typename TLazyIn>
auto constexpr lazyCast(TLazyIn in){
return lazyCastImpl<Tout, TLazyIn>(in);
}
template <typename TLazyChar>
auto constexpr lazyUpper(TLazyChar in){
return lazyUpperImpl<TLazyChar>(in);
}
template <typename TLazyNumeric>
auto constexpr lazyAdd5(TLazyNumeric in){
return lazyAdd5Impl<TLazyNumeric>(in);
}
int foo(int in){
auto lazyInt = [in](){return in;};
auto x =
lazyAdd5(
lazyCast<int>(
lazyUpper(
lazyCast<char>(lazyInt)
)
)
) ();
return x;
}
int main(){
return foo(109);
}
With gcc 10.2
, clang 10.0.1
and msvc 19.24
the code for foo
becomes a simple set of instructions - conditionally subtract 26 and always add 5.
For example, the assembly generated by msvc
is:
movzx eax, cl
cmp cl, 97 ; 00000061H
jl SHORT $LN26@foo
cmp cl, 122 ; 0000007aH
jg SHORT $LN26@foo
lea eax, DWORD PTR [rcx-32]
$LN26@foo:
movsx eax, al
add eax, 5
ret 0
The output from msvc
is arguably the least elaborate (and thus most easy to understand) of the three.
Moreover, if you begin with an input value known at compilation time, this is inlined to a single return new_value
instruction.
Note that in the example above, the compiler cannot avoid the if
condition, and the 'lazyCast' is just a no-op.
Here is another interesting example where two if
statements and two mathematic expressions are simply canceled out.
For real programs, actual efficiency is very difficult/impossible to predict from the assembly - it depends on the machine executing the program, and on the state this machine is in (e.g. is it running different programs simultaneously?). Even then, you'll need to run test to be sure which lines work best.
And, of course, best is subjective. You'll need to decide if you prefer optimizing the running time(which may be broken down even further), the executable file's size or the power requirements?
This requires a lot of studying to get right, but you can rest assured that there are some very talented people working on these questions for a living, and they generally do a very good job.