I have been looking for a design that solves the following problem.
It will take a few words to describe.
We have four types A1, A2, B, and C.
We would like to write a function fn that takes as parameter a type P.
Using traits, P resolves within A1/A2/B/C to PA1/PA2/PB/PC.
The implementation of fn is the same for PA1 and PA2, but it is
different for PB and PC, both between them as well as from that of
PA1/PA2.
#include <cassert>
struct PA1 {};
struct PA2 {};
struct PB {};
struct PC {};
struct A1 { typedef PA1 P; };
struct A2 { typedef PA2 P; };
struct B { typedef PB P; };
struct C { typedef PC P; };
template<typename T> char fn(typename T::P)
{
return 'a';
}
char fn(B::P) { return 'b'; }
char fn(C::P) { return 'c'; }
int main()
{
PA1 pa1;
PA2 pa2;
PB pb;
PC pc;
assert( fn<A1>(pa1) == 'a' );
assert( fn<A2>(pa2) == 'a' );
assert( fn(pb) == 'b' );
assert( fn(pc) == 'c' );
}
The advantage of the code above is that the implementation of fn for
PA1 and PA2 is not repeated.
But here is the snag. The function calls are not symmetric. It is
fn<A1>(pa1)
and
fn<A2>(pa2)
for A1/A2/PA1/PA2, but just fn(pb) and
fn(pc) for B/C/PB/PC.
This precludes using fn(..) within yet another (not shown) class
template.
Normally this is not an issue. The template parameter can be deduced
for parameterized functions. That doesn’t work here. We’d be asking
the compiler to locate the type among A1/A2/B/C for which P resolves
to one of PA1/PA2/PB/PC.
What would you do?
I think that you are confusing two things here:
Does not preclude
PA1andPA2from being distinct types with distincttypedef.Which we can therefore use as is:
If you cannot actually modify
Pitself, you can always introduce a traits class.I would note that Herb Sutter recommend not to use function specializations and to prefer overload, because the interaction between overload (esp. with template involved) is specialization is extremely tricky… and it’s hard to fathom which function will get called from a given set of parameters.