开发者

Transforming a parameterization by two classes to a parameterization by one class

The following code has a little bit of unpleasantness.

#include <cassert>

template<typename S> struct PA1 {}; template<typename S> struct QA1 {};
template<typename S> struct PA2 {}; template<typename S> struct QA2 {};
template<typename S> struct PB  {}; template<typename S> struct QB  {};
template<typename S> struct PC  {}; 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 PA, typename QA>
char fn(PA, QA) {
    return 'a';
}

template<typename S> char fn(PB<S>, QB<S>) { return 'b'; }
template<typename S> char fn(PC<S>, QC<S>) { return 'c'; }

template<typename T>
struct Action
{
    char z;
    Action(typename T::P p, typename T::Q q)
    {
        z = fn(p, q);
    }
};

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;

    assert( fn(pa1, qa1) == 'a' );
    assert( fn(pa2, qa2) == 'a' );

    assert( fn(pb, qb) == 'b' );
    assert( fn(pc, qc) == 'c' );

    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' );
}

Even though PA and QA always arise in tandem (PA1 with QA1 and PA2 with QA2), we 开发者_Python百科write

template<typename PA, typename QA>
char fn(PA, QA) { ... }

It would be much nicer to write

template<typename A>
char fn(typename A::P, typename A::Q) {
    return 'a';
}

Can you suggest a modification that would make that possible?


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' );
}


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.


C++0x will have template aliases

Oh and to track when it comes to GNU C++: http://gcc.gnu.org/projects/cxx0x.html

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜