我想建立以下课程。基类定义要实现的功能,派生实现此接口。
template <class T, class V>
class IBase
{
public:
virtual void foo(const typename V::t_args&) =0;
};
template<class T>
struct T_args
{
T z;
};
class Derived : public IBase<double, Derived>
{
public:
typedef T_args<double> t_args;
Derived() {}
void foo(const t_args& x)
{ /* do some stuff */ }
};
编译器抱怨Derived是不完整的类型。我不明白原因。 有什么办法使这个类结构正确吗?
我被迫使用c ++ 98进行编码,但是我对c ++ 11及更高版本中的任何解决方案都感兴趣。
答案 0 :(得分:5)
在基本模板类中:
virtual void foo(const typename V::t_args&) =0;
这是引用其t_args
模板参数的某个内部类或类型V
的类型。引用类成员时,该类的定义必须完整(以弄清楚t_args
是什么)。您正在尝试按以下方式使用此模板类:
class Derived : public IBase<double, Derived>
您要为Derived
传递V
,但是其类定义不完整。如果模板基类仅引用了其V
参数,则通常为“ ok”。但是,您的模板要求其模板参数类型完整,因为它需要知道t_args
到底是什么,并且您的派生类在完全定义之前是不完整的。但是,直到完全定义其基类后,才能完全定义它。有点像鸡与蛋的情况。
对于这种类型的循环引用并没有交钥匙解决方案。唯一可以做的就是重组类,因此您的“参数”类型是一个独立的类,而不是派生类。
答案 1 :(得分:1)
另一种解决方法是使用某些特征类:
// The traits class
template <typename T> struct Arg;
template <class T, class V>
class IBase
{
public:
virtual ~IBase() {}
virtual void foo(const typename Arg<V>::t_args&) = 0; // V can be incomplete here
// but Arg<V> should be complete
};
// So go to define Arg<Derived>:
// Result class
template<class T>
struct T_args
{
T z;
};
// Forward declaration, Arg<V> accept incomplete type
class Derived;
// Specialization for Derived
// should not use internal of Derived as it is incomplete
template <>
struct Arg<Derived>
{
typedef T_args<double> t_args;
};
// Now definition of Derived
class Derived : public IBase<double, Derived>
{
public:
typedef Arg<Derived>::t_args t_args; // Should probably go in IBase for ease usage
Derived() {}
void foo(const t_args& x) /* override */
{ /* do some stuff */ }
};
答案 2 :(得分:0)
在阅读explanation by Sam Varshavchik之后,可以通过在基类的模板签名中添加StringSignature
类来解决我的问题:
t_args