C ++:多态类模板

时间:2009-10-11 10:32:24

标签: c++ inheritance templates polymorphism

考虑一个存储一堆Date对象的类Calendar。 该日历旨在保存从Date继承的任何类型的对象的集合。我认为最好的方法是使用类模板,如

template<typename D> class Calendar{ 
    ...
}

但让我感到震惊的是,D现在实际上可以成为任何阶级。 我现在的问题是,如何确保D是日期对象的子类?

我知道如何做到这一点是Java,但我仍然不熟悉C ++语法。问题非常类似于某些集合只能采用实现Comparable的模板变量。然后标题看起来像

public class Calendar<D extends Date>{
     ...
}

--------------------编辑:------------------------- -----------------

template参数定义日历引用的实际日期。不同的日期类型以不同的格式表示同一天。例如,如果我创建Calendar<Gregorian>,它将能够以另一种Date格式,比如朱利安日历或任何其他日期格式拍摄日期,并以格里高利格式呈现。这样可以在不同日期格式的日历之间进行转换。所以,如果我有一个Calendar<Gregorian>,我可以很容易地将其转换为Calendar<Julian>。那么以下是可能的:

Calendar<Gregorian> cal;
std::cout << "These events are entered as dates in 
    the Gregorian calendar" << std::endl;
cal.add_event("Christmas", 12, 25);
cal.add_event("Gregorian new year", 1, 1);
std::cout << cal << std::endl;
std::cout << "----" << std::endl;
std::cout << "And printed out as Julian dates" << std::endl;
Calendar<Julian>(cal);
std::cout << cal<< std::endl;

和输出:

These events are entered as dates in the Gregorian calendar
2009-12-25 Christmas
2010-01-01 Gregorian new year
----
And printed out as Julian dates
2009-12-13 Christmas
2009-12-19 Gregorian new year

-------------新编辑:----------------------

现在最后一次编辑更有意义。我对格式有点不同意。

感谢所有答案。

我是第三年的计算机科学专业的学生,​​我会说我对OO和相关概念如Polymorphism等非常熟悉。这篇文章的目的是找出是否有办法在C ++中表达模板参数的条件与在Java中相同,并以简洁,优雅和直观的方式解决问题。

6 个答案:

答案 0 :(得分:9)

  

我知道如何做到这一点是Java,但我仍然不熟悉C ++语法。问题非常类似于某些集合只能采用实现Comparable的模板变量。然后标题看起来像

public class Calendar<D extends Date>{
     ...
}

是的,这是同样的问题,在C ++中,通常通过忽略它来解决。为什么我们需要强制对象必须实现IComparable?在Java中,由于其贫血型系统,它是必要的。没有这个约束,我们将无法比较对象。

在C ++中,规则是不同的。容器只需尝试来比较它们存储的对象,如果类型不支持它,则会出现编译错误。不需要接口或继承。

你通常会在Calendar课程中做同样的事情。只需不强制“必须子类形式Date约束。

相反,请指定类型必须公开的成员,以及应该从中获取的语义(如果有的话)。

例如,如果您的日历尝试执行以下操作,则对于日期对象d0d1

d0.getDay();
d0.getTime();
Time t = d0 - d1;

然后这些是应该支持的操作。任何支持这些操作的类都是一个有效的Date类,即使它没有任何的子类。

答案 1 :(得分:8)

您正在寻找的是模板参数的概念检查。这些已成为下一个C ++标准草案的一部分,但几周/几个月前又被抛弃了。

如果没有适当的语言概念,有一些库试图这样做,但是想要概念检查作为核心语言的一部分的原因是,如果没有语言支持,它或多或少是不可能实现的。

在你的具体例子中,这不应该太难。例如,您可以将一些特殊的typedef放入基类并检查:

class date {
  public:
    typedef int is_derived_from_date;
};

template<typename D> class Calendar{ 
    typedef typename D::is_derived_from_date blah;
    ...
};

另一种方法是选择在网络上浮动的任何is_derived<B,D>::result模板元函数,并在Calender类中对此进行静态检查。 Boost同时具有is_derived元函数和静态断言。

说完这一切之后,我不得不质疑你的设计。普通的OO多态性有什么问题,你想使用模板的编译时多态?

答案 2 :(得分:3)

我认为您的问题可以在不使用模板的情况下解决。 D始终是Date的派生类,那么为什么不只是拥有Date对象的集合?

答案 3 :(得分:2)

在C ++中,这称为概念检查。在C ++中,一个常见的最佳实践是继承用于继承接口而不是实现。因此,您对D是否继承Date并不感兴趣,但D是否具有您需要的Date的界面元素,即它具有必要性这样做的好处是,您不需要将来的D类继承自Date,它们只需要实现某些功能。

概念检查已在C ++ 0x中删除,但您可以在Boost.ConceptCheck中找到它(Boost主站点为here)。

如果您确实希望强制D继承自Date,则可以将Boost.StaticAssertBoost.TypeTraits结合使用来检查D是否继承自Date {{1}}。

答案 4 :(得分:1)

模板通常不需要继承/多态限制。模板旨在与满足给定要求的任何类型一起使用,无论基类型如何。

template <typename T>
T clone(const T& cloneable) {
    return cloneable.create_clone();
} 

此代码适用于支持create_clone()操作的任何类型,不使用ICloneable - 接口!

在您的情况下,此代码将允许使用任何类似日期的类型。

如果您想要基类多态,只需将模板保留并使用Date*

请注意,如果您确实想要进行模板测试,可以尝试将虚拟对象指针强制转换为Date*,如果它不是Date的派生,则会在编译时失败。但这通常不是模板代码的使用方式。

答案 5 :(得分:0)

如果您只想与Date对象进行交互,那么为什么不使用普通多态并只处理Date * -s?

如果你打算在Calendar中有一个日期集合,它可以包含不同Date子类的实例,那么我怀疑模板是否会起作用,除了多态之外你什么也没有帮助你。

对于模板,如果给定类型具有合适的接口,为什么它不适用于日历? (好吧,概念是为C ++ 0x计划的,但已经下降了,但他们的主要动机似乎是允许更清晰的与模板相关的错误消息。)