I'd like to add compile-time checking for different meanings of double
. In the real world, I'm trying to make sure all calculations are done in consistent units. For the purposes of this question, I've concocted a toy example in which numbers have flavors.
I've been trying to achieve this based on a template parameter. Using the c++0x aliases feature described in another answer, I declared a Number<Flavor>
as:
enum Flavor { Cherry, Plum, Raspberry };
template <Flavor> using Number = double;
This gives me the ability to declare local variables or parameters as particular flavors of Number, then use those variables as ordinary doubles in most contexts.
My problem is, I can't find a way to declare a function that will only accept a particular flavor as its argument:
void printCherryNumber(Number<Cherry> num) { cout << num << endl; }
int main() {
Number<Cherry> a(5);
Number<Plum> b(6);
Number<Raspberry> c(3.1415);
printCherryNumber(a);
printCherryNumber(b); // O, if only this could be a compiler error.
return 0;
}
My goal is to make printCherryNumber(b)
fail to compile because b
is a Number<Plum>
not Number<Cherry>
. Many existing questions tackle variations on this problem with solutions that seem to not work on the type alias construct I've used for Number
.
Stuff I've Tried
From this answer, I see the suggestion to add a templated version of the function that explicitly does nothing or breaks, as in
template <typename T> void printCherryNumber(T num) = delete;
This has no effect at all, and why should it? Number<Plum>
is really double
and Number<Cherry>
is also double
so the compiler never bothers with the templated version.
Another answer suggests using a single templated function and static asserts, as in:
template <Flavor F> void printPlumNumber(Number<F> num) {
static_assert(F == Plum, "Wrong number flavor!");
cout << num << endl;
}
This fails because regardless of the actual value of F
, Number<F>
is still just double
and so I get an error about not being able to infer the value of F
.
Elsewhere someone suggests explicit specialization, which also fails for this case:
template <Flavor F> void printRaspberryNumber(Number<F> num) = delete;
template <> void printRaspberryNumber<Raspberry>(Number<Raspberry> num) {
cout << num << endl;
}
Here, the compiler treats the call as ambiguous, in part again because it can't infer a value for F
.
Elephant in the Room
I could, of course, make Number
a single-value struct in the form of
template <Flavor> struct Number { double value; };
but I'm trying to avoid this option because I'm not terribly thrilled about the idea of having .value
all over everywhere in my code, nor am I especially eager to define operators for Number
that just proxy down to double.