我有以下类的结构和模板化的包装类。
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>*
。是否有任何解决过程功能的工作原理?
答案 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);
}