开发者

Infer subclass template types in a template

I have a class that derives from a templated class:

template <typename A,typename B>
class TemplatedClass {

};

class Hello : public TemplatedClass<int,float>
{

};

Now, I want to make a templated class that will infer the types int,float from Hello.

I thought I could do something like this, but it doesn't work:

template <template <typename A,typename B> class C>
class Check
{
    void Foo(A,B,C)开发者_JAVA百科
    {
        // A is int .. B is float .. C is Hello
    }
};



int _tmain(int argc, _TCHAR* argv[])
{
    Check<Hello> a;
}

How can i do this ?

Edit:

I want to pass the class Hello and have the template infer the types used by its subclass TemplatedClass .

So, when I create a class Check<Hello> it will get the types int and float

I can't really change the TemplatedClass to include typedefs (it's from an an external .lib)

Edit:

I've changed the template to use Class , I get this error though:

error C3200: 'Hello' : invalid template argument for template parameter 'C', expected a class template


First, change that typename to class, §14.1 [temp.param] p1:

type-parameter:

  • class identifieropt
  • class identifieropt = type-id
  • typename identifieropt
  • typename identifieropt = type-id
  • template <template-parameter-list > class identifieropt
  • template <template-parameter-list > class identifieropt = id-expression

Next, make a partial specialization:

template<class T>
class Check;

template< // not 'typename'   vvvvv
  template<typename,typename> class C,
  typename A, typename B
>
struct Check<C<A,B> >{
  // ...
};

Though, you still can't pass just Hello to the template, because even though Hello derives from TemplatedClass, type conversion is not allowed for template parameters:

Check<Hello> c; // nope, 'Hello' is not a template

You could add the following typedef to the Hello class:

class Hello
  : TemplatedClass<int,float>
{
public:
  typedef TemplatedClass<int,float> base_type;
};

And do:

Check c; // OK But then the C parameter in Check will be template TemplatedClass, not Hello. Sadly, there's no way to achieve that directly. One solution is to pass the derived type either as an extra template parameter or just pass the derived type as the only parameter and do the type extraction internally:

template<class T>
class CheckInternal;

template<
  template<typename,typename> class C,
  typename A, typename B
>
class CheckInternal<C<A,B> >{
  public:
     typedef A type_A;
     typedef B type_B;
};

template<class T>
class Check{
  typedef typename T::base_type T_base_type;
  typedef typename CheckInternal<T_base_type>::type_A type_A;
  typedef typename CheckInternal<T_base_type>::type_B type_B;

  void foo(type_A a, type_B b){
    // ...
  }
};

// usage:
C<Hello> c; // OK!


You could do something like this:

template <template <typename A,typename B> typename C>
class Check
{
     template<typename A, typename B>
     void Foo(A a, B b) {
        // C<A,B> would reconstruct the template type
     }
}

// use: 
Check<Hello> a;
a.Foo(true,1.f);

or alternatively, this (it's not exactly clear what your intent is):

template <typename A,typename B>
class TemplatedClass {
   typedef A TypeA;
   typedef B TypeB;
};


template <typename C>
class Check
{

     void Foo(typename C::TypeA& a, typename C::TypeB&) {}
}

// use: 
Check<Hello<int,float> > a;
a.Foo(1,1.f);


Try something like:

template <typename A,typename B>
class TemplatedClass {
public:    
    typedef A firstType;
    typedef B secondType;
};

class Hello : public TemplatedClass<int,float>
{
public:    
    typedef firstType Type1;
    typedef secondType Type2;
};

template <typename C>
class Check
{
    typedef typename C::Type1 A;
    typedef typename C::Type2 B;

    void Foo(A,B,C)
    {
        // A is int .. B is float .. C is Hello
    }
};


I think you can do it by creating a templated class for Hello and then typedefing a specialization of it. Something like:

template <typename A, typename B>
class HelloBase : public TemplatedClass<A, B>
{
public:    
    typedef A Type1;
    typedef B Type2;
};

typedef HelloBase<int, float> Hello;

template <typename C>
class Check
{
    typedef typename C::Type1 A;
    typedef typename C::Type2 B;

    void Foo(A,B,C)
    {
        // A is int .. B is float .. C is Hello
    }
};

...

Check<Hello> a;

So TemplatedClass doesn't need to change, and you can put everything you were going to put in Hello into HelloBase (using the templating as a tool for simply carrying around types).


What you want is impossible in the general case- what if Hello derived from TemplatedClass twice?

It is, however, fairly simple to do, even non-intrusively, under C++0x.

template<typename A, typename B> struct retval {
    typedef A first;
    typedef B second;
};
template<typename one, typename two> one first(const TemplatedClass<one, two>& ref);
template<typename one, typename two> two second(const TemplatedClass<one, two>& ref);
template<typename T> class Check {
    typedef decltype(first(*static_cast<T*>(nullptr))) first;
    typedef decltype(second(*static_cast<T*>(nullptr))) second;
};

In C++03 then you can still get the parameters inside the method, but can't access them outside, unless you insert special typedefs.

0

上一篇:

下一篇:

精彩评论

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

最新问答

问答排行榜