虽然B
是 a-kind-of A
,Foo<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);
}