2

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

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>*. 是否有任何解决方法来修复流程功能?

4

2 回答 2

2

制作process函数模板:

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

在函数模板中,您可以使用 typename T,就好像它是 class 一样A。然后,函数模板也适用于所有调用Foo<B>

于 2012-11-09T09:30:48.283 回答
0

虽然B是 a-kind-of AFoo<B>但不是 a-kind-of 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);
}
于 2012-11-09T10:40:58.540 回答