Templates in C++ allow runtime work to be shifted to compile time. I have a variable which will be known at runtime, but the set of its possible values is known at compile time. My questions:
- Since the possible values are known at compile time, can I use this knowledge to optimize?
- If so, is the below how one would do this? If not, how can I do it?
- If it is, can someone explain why/what this optimizes exactly? i.e. what work is the compiler doing in my second example that would have otherwise been done at runtime in the first? It isn't immediately clear to me.
What I'm trying to accomplish:
#include <iostream>
using namespace std;
void actionA() {
cout << "doing custom action for a" << endl;
}
void actionB() {
cout << "doing custom action for b" << endl;
}
void actionC() {
cout << "doing custom action for c" << endl;
}
int main() {
char op;
while (cin >> op) {
switch(op) {
case 'a':
actionA();
break;
case 'b':
actionA();
break;
case 'c':
actionA();
break;
default:
return EXIT_FAILURE;
};
}
}
Template specializations for each possible value:
#include <iostream>
using namespace std;
template <char>
void action() {
// EXIT_FAILURE
}
template <> void action<'a'>() { cout << "doing custom action for a" << endl; }
template <> void action<'b'>() { cout << "doing custom action for b" << endl; }
template <> void action<'c'>() { cout << "doing custom action for c" << endl; }
int main() {
char op;
while (cin >> op) {
switch(op) {
case 'a':
action<'a'>();
break;
case 'b':
action<'b'>();
break;
case 'c':
action<'c'>();
break;
default:
return EXIT_FAILURE;
};
}
}
That's not actually true. You do not know the possible values at compile-time. That's why you have a
default
branch to handle the remaining cases.But regardless, you are already telling the compiler in both examples on which specific compile-time values actions are necessary. There is no extra information you could give the compiler here.
There is nothing to optimize in the code that you are showing. It is limited by IO and there isn't actually any function that acts on a runtime value where it would make sense to give the compiler additional compile-time information about the value.
It won't do anything different. You just effectively renamed the functions.
A better example is something like a function
void action(char c)
which does some expensive calculation for generalc
and is called in each of thecase
s of theswitch
.In that case it may make sense to replace the function by
template<char c> void action()
with the same body and call it as in your second example instead.This would then cause the function to be emitted once for each of the possible
c
values, and each such specialization can then be optimized easily by the compiler individually with the additional knowledge of a fixedc
.Of course, the compiler is always free to make such a transformation regardless by cloning the function itself, but it is non-obvious to the compiler where this is an optimization and where not.