我的代码如下。我有一个抽象模板类Foo和两个子类(Foo1和Foo2),它们派生自模板的实例化。我希望在我的程序中使用指向Foo1或Foo2类型的对象的指针,因此我创建了一个接口IFoo。
我的问题是我不确定如何在界面中包含functionB,因为它依赖于模板实例化。甚至可以通过界面使functionB可访问,还是我尝试不可能?
非常感谢你的帮助。
class IFoo {
public:
virtual functionA()=0;
};
template<class T>
class Foo : public IFoo{
public:
functionA(){ do something; };
functionB(T arg){ do something; };
};
class Foo1 : public Foo<int>{
...
};
class Foo2 : public Foo<double>{
...
};
答案 0 :(得分:15)
你实际上是在尝试不可能的事。
问题的核心很简单:virtual
和template
混合不好。
template
是关于编译时代码生成的。您可以将其视为某种类型感知宏+一些用于元编程的技巧。virtual
是关于运行时决定的,这需要一些工作。 virtual
通常使用虚拟表实现(想象一下列出方法的表)。需要在编译时知道方法的数量,并在基类中定义。
但是,根据您的要求,我们需要一个无限大小的虚拟表,其中包含我们尚未见过的类型的方法,并且只会在未来几年内定义......遗憾的是不可能。
如果可能的话?
嗯,这没有意义。使用Foo2
致电int
时会发生什么?它并不意味着它!因此,它打破了Foo2
实现IFoo
的所有方法的原则。
所以,如果你说出真正的问题会更好,这样我们可以在设计层面而不是技术层面帮助你:)
答案 1 :(得分:6)
最简单的方法是使您的界面模板化。
template <class T>
class IFoo {
public:
virtual void functionA()=0;
virtual void functionB(T arg){ do something; };
};
template<class T>
class Foo : public IFoo<T>{
public:
void functionA(){ do something; };
void functionB(T arg){ do something; };
};
答案 2 :(得分:4)
由于必须事先知道functionB的参数类型,因此您只有一个选择:将其设置为可以包含每个可能的参数的类型。这有时被称为“顶级类型”,并且boost库具有any
类型,它与顶级类型非常接近。这是可行的:
#include <boost/any.hpp>
#include <iostream>
using namespace boost;
class IFoo {
public:
virtual void functionA()=0;
virtual void functionB(any arg)=0; //<-can hold almost everything
};
template<class T>
class Foo : public IFoo{
public:
void functionA(){ };
void real_functionB(T arg)
{
std::cout << arg << std::endl;
};
// call the real functionB with the actual value in arg
// if there is no T in arg, an exception is thrown!
virtual void functionB(any arg)
{
real_functionB(any_cast<T>(arg));
}
};
int main()
{
Foo<int> f_int;
IFoo &if_int=f_int;
if_int.functionB(10);
Foo<double> f_double;
IFoo &if_double=f_double;
if_int.functionB(10.0);
}
不幸的是,any_cast
不知道通常的转化。例如any_cast<double>(any(123))
抛出一个异常,因为它甚至没有尝试将整数123转换为double。如果不关心转换,因为无论如何都不可能复制所有转换。因此存在一些限制,但如有必要,可以找到解决方法。
答案 3 :(得分:0)
我认为你不能得到你想要的东西。如果您要实现您的建议,请考虑这一点:如果您有一个指向IFoo
实例的指针并且您调用了functionB()
,那么您应该给出什么类型参数?根本问题是Foo1::functionB
和Foo2::functionB
具有不同的签名并执行不同的操作。
答案 4 :(得分:0)
通过将IFoo *指针包装在类中并通过非模板化包装类的通用模板函数公开功能,可以实现类似的功能:
#include <assert.h>
// interface class
class IFoo {
public:
virtual int type() const = 0; // return an identifier for the template parameter
virtual bool functionA() = 0;
};
// This function returns a unique identifier for each supported T
template <typename T> static int TypeT() { static_assert("not specialized yet"); }
template <> static int TypeT<bool>() { return 0; }
template <> static int TypeT<double>() { return 1; }
//template <> static int TypeT<...>() { ... }
// templated class
template <typename T> class FooT : public IFoo {
public:
int type() const override { return TypeT<T>(); }
bool functionA() override { return true; }
// not in interface
bool functionB(T arg) { return arg == T(); }
};
// function to create an instance of FooT (could also be static function in FooT)
static IFoo* CreateFooT(int type)
{
switch (type)
{
case 0: return new FooT<bool>();
case 1: return new FooT<double>();
//case ...: return new FooT<...>();
default: return nullptr;
}
}
// Non-templated wrapper class
class FooWrapper {
private:
IFoo *pFoo;
public:
FooWrapper(int type) : pFoo(CreateFooT(type)) { assert(pFoo != nullptr); }
~FooWrapper() { delete pFoo; }
bool functionA() { return pFoo->functionA(); }
template <typename T> bool functionB(T arg)
{
if(pFoo->type() != TypeT<T>())
{
assert(pFoo->type() == TypeT<T>());
return false;
}
return static_cast<typename FooT<T>*>(pFoo)->functionB(arg);
}
// fun stuff:
// (const pendants omitted for readability)
bool changeType(int type)
{
delete pFoo;
pFoo = CreateFooT(type);
return pFoo != nullptr;
}
IFoo* Interface() { return pFoo; }
IFoo* operator->() { return pFoo; }
operator IFoo&() { return *pFoo; }
template <typename T> FooT<T> *InterfaceT()
{
if(pFoo->type() != TypeT<T>())
{
assert(pFoo->type() == TypeT<T>());
return nullptr;
}
return static_cast<typename FooT<T>*>(pFoo);
}
};
int main(int argc, char *argv[])
{
FooWrapper w1(TypeT<bool>());
FooWrapper w2(TypeT<double>());
w1.functionA(); // ok
w2.functionA(); // ok
w1.functionB(true); // ok
w1.functionB(0.5); // runtime error!
w2.functionB(true); // runtime error!
w2.functionB(0.5); // ok
// fun stuff
w2.changeType(TypeT<bool>()); // older changes will be lost
w2.functionB(true); // -> now ok
w1.Interface()->functionA();
w1->functionA();
IFoo &iref = w1;
iref.functionA();
FooT<bool> *ref = w1.InterfaceT<bool>();
ref->functionB(true);
return 0;
}
当然您有责任使用正确的类型调用函数,但您可以轻松添加一些错误处理。