带有basetype实例化的模板参数的C ++函数

时间:2012-11-09 09:27:06

标签: c++ templates

我有以下类的结构和模板化的包装类。

class A {};
class B : public A {};

template<class T>
class Foo {
  T val;
public:
  Foo(T t) {val = t};
  T get() {return val};
}

现在,我需要一个函数来处理存储在模板中的值,但我无法修改模板本身。我想要的是一个具有类似于

的签名的函数
void process(Foo<A>*) {/* do something */}

我希望像

一样使用它
B b; Foo<B> foo(b);
process(&foo);

但是,这不起作用,因为编译器不知道如何将Foo<B>*转换为Foo<A>*。是否有任何解决过程功能的工作原理?

2 个答案:

答案 0 :(得分:2)

使process成为一个功能模板:

template <typename T> void process(Foo<T>*)

在函数模板中,您可以使用类型名T,就好像它是类A一样。然后,功能模板也可用于Foo<B>的所有呼叫。

答案 1 :(得分:0)

虽然B属于A,但Foo<B>不属于Foo<A>

苹果是一种水果。是一袋苹果也是一袋水果?不,因为你可以把梨放在一袋水果里。

在你的情况下,你不能在你的包装器中创建任何东西,所以这个参数不起作用。但是C ++编译器并不够聪明,无法解决这个问题。在像Scala这样的其他语言中,你实际上可以做到这一点。

您可以将process设为模板,但有时您不能,例如它是某个类中的虚函数。

通过一些易于中等的手动工作和模板元编程,可以让Foo<Derived>Foo<Base>一样工作。手动工作涉及将Foo拆分为接口和实现部分。

// Instead of class B : public A, use class B : public DiscoverableBase<A>
// Doesn't cost you anything. Won't work with multiple inheritance, where 
// scattered-inheritance or similar technique must be adopted.

#include <cstdlib> 
template <typename B>
struct DiscoverableBase : public B
{
    typedef B Base;
};

struct Void {};

// This fetches Base member type out of T, or Void if there's none.
// Probably could be simplified a bit.
template <typename T>
class BaseOf
{
    typedef char yes;
    typedef struct { char a[2]; } no;

    template <typename Q, size_t> struct GetBase;

    template <typename Q>
    struct GetBase<Q, sizeof(no)>
    {
        typedef Void Base;
    };

    template <typename Q>
    struct GetBase<Q, sizeof(yes)>
    {
        typedef typename T::Base Base;
    };

    template <typename C> static yes check(typename C::Base*) ;
    template <typename C> static no  check(...);
  public:
    typedef typename GetBase<T, sizeof(check<T>(0))>::Base Base;
};

// This is how you use DiscoverableBase.
class A {};
class B : public DiscoverableBase<A> {};

// Foo is a bit more complicated now. 
template<typename T> class Foo;

// The base case, inherited by Foo<X> where X has no discoverable base
template<> class Foo<Void> {};

// This is the Foo interface. Note how it retuns a reference to T now.
template<typename T>
class Foo : public Foo<typename BaseOf<T>::Base>
{
  public:
    virtual T& get() = 0;
};

// This is the Foo implementation.
template<typename T>
class FooImpl : public Foo<T>
{
    T val;
  public:
    FooImpl(T t) {val = t;}
    T& get() {return val;}
};

// the rest is mostly unchanged
void process(Foo<A>*) {/* do something */}

int main()
{
    B b; FooImpl<B> foo(b);
    process(&foo);
}