The following code has a little bit of unpleasantness.
#include
template struct PA1 {}; template struct QA1
As I understand the question and its predecessors, the task is a specialization of function template on a class template regardless of its template parameter.
Generic solution:
#include <cassert>
//remember the value of the argument S to retrieve it later
template<typename S> struct PA1 { typedef S S; };
template<typename S> struct PA2 { typedef S S; };
template<typename S> struct PB { typedef S S; };
template<typename S> struct PC { typedef S S; };
//helper: generic version of fn for any parameters except PB и PC
template<typename T, typename S>
struct FN
{
static char fn() { return 'a'; }
};
//helper: fn specialized for class template PB
template<typename S>
struct FN<PB<S>, S>
{
static char fn() { return 'b'; }
};
//helper: fn specialized for class template PC
template<typename S>
struct FN<PC<S>, S>
{
static char fn() { return 'c'; }
};
//fn relies on compiler's type deduction to avoid specifying of template parameter explicitly
template<typename T>
char fn(T t)
{
return FN< T, T::S>::fn();
}
//usage
int main()
{
PA1<int> pa1;
PA2<char> pa2;
PB<float> pb;
PC<double> pc;
assert( (fn(pa1)) == 'a' );
assert( (fn(pa2)) == 'a' );
assert( (fn(pb)) == 'b' );
assert( (fn(pc)) == 'c' );
}
Applying this method to your case:
#include <cassert>
template<typename S> struct PA1 { typedef S S; }; template<typename S> struct QA1 {};
template<typename S> struct PA2 { typedef S S; }; template<typename S> struct QA2 {};
template<typename S> struct PB { typedef S S; }; template<typename S> struct QB {};
template<typename S> struct PC { typedef S S; }; template<typename S> struct QC {};
template<typename S> struct A1 { typedef PA1<S> P; typedef QA1<S> Q; };
template<typename S> struct A2 { typedef PA2<S> P; typedef QA2<S> Q; };
template<typename S> struct B { typedef PB<S> P; typedef QB<S> Q; };
template<typename S> struct C { typedef PC<S> P; typedef QC<S> Q; };
template<typename T, typename S>
struct FN
{
static char fn() { return 'a'; }
};
template<typename S>
struct FN<PB<S>, S>
{
static char fn() { return 'b'; }
};
template<typename S>
struct FN<PC<S>, S>
{
static char fn() { return 'c'; }
};
template<typename A>
char fn() //or char fn(A* a)
{
return FN<A::P, A::P::S>::fn();
}
template<typename T>
struct Action
{
char z;
//so the constructor accepts only correct combinations of p and q
Action(typename T::P p, typename T::Q q)
{
z = fn<T>(); //or fn((T*)NULL);
}
};
int main()
{
PA1<int> pa1; QA1<int> qa1;
PA2<int> pa2; QA2<int> qa2;
PB<int> pb; QB<int> qb;
PC<int> pc; QC<int> qc;
Action<A1<int> > aa1 = Action<A1<int> >(pa1, qa1); assert( aa1.z == 'a' );
Action<A2<int> > aa2 = Action<A2<int> >(pa2, qa2); assert( aa2.z == 'a' );
Action<B<int> > ab = Action<B<int> >(pb, qb ); assert( ab.z == 'b' );
Action<C<int> > ac = Action<C<int> >(pc, qc ); assert( ac.z == 'c' );
}
C++0x will have template aliases
Oh and to track when it comes to GNU C++: http://gcc.gnu.org/projects/cxx0x.html
If you want type A
to be automatically inferred, then no such modification is possible unless you have fn
take an argument of type A
(or A&
or A*
etc) and consequently force the user to pass in a value of that type.
On the other hand, if you don't mind forcing the caller to specify the template argument explicitly, then that syntax is already fine.
There's no happy medium.