【问题标题】:C++: Polymorphic class templateC++:多态类模板
【发布时间】:2010-12-05 17:48:15
【问题描述】:

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

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

但令我震惊的是,D 现在实际上可以是任何类。 我现在的问题是,如何确保 D 是日期对象的子类?

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

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

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

模板参数定义日历所指的实际日期。不同的日期类型以不同的格式指代同一天。例如,如果我创建一个Calendar&lt;Gregorian&gt;,它将能够以另一种Date 格式获取日期,例如儒略历或任何其他日期格式,并以公历格式显示它们。这允许在不同日期格式的日历之间进行转换。所以,如果我有Calendar&lt;Gregorian&gt;,我可以轻松地将其转换为Calendar&lt;Julian&gt;。那么以下是可能的:

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 和多态等相关概念相当熟悉。这篇文章的目的是找出是否有办法在 C++ 中以与在 Java 中相同的方式表达模板参数的条件,并以简洁、优雅和直观的方式解决问题。

【问题讨论】:

  • 为什么它需要成为 Date 的子类?只要它表现得像一个日期(暴露正确的成员),把它当作一个日期有什么问题?

标签: c++ inheritance templates polymorphism


【解决方案1】:

我知道如何做到这一点是 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 类,即使它没有子类任何东西

【讨论】:

  • IMO 这是迄今为止最好的答案。我希望我的回答只是说鸭子打字很好。 +1
【解决方案2】:

您正在寻找的是模板参数的概念检查。这些是下一个 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&lt;B,D&gt;::result 模板元函数,并在您的Calender 类中对此进行静态检查。 Boost 具有 is_derived 元函数和静态断言。

然而,说了这么多,我不得不质疑你的设计。要使用模板的编译时多态性,普通的OO多态性有什么问题?

【讨论】:

  • 回复。最后一段,模板的编译时多态性有什么问题,要使用普通的OO多态性?我同意,混合很尴尬。从我们掌握的信息来看,动态和静态多态性似乎都可以工作。然后我更喜欢静态的。
  • @jalf:我也可能。但是从这个问题来看,Marco 似乎已经非常了解 OO 多态性,而 IME 很多人需要很长时间才能理解高级模板的东西和静态多态性。因此,对于了解 OO 的 C++ 初学者,我建议坚持这一点,直到他们对 C++ 的操作方式更加熟悉为止。
  • 真的。任何一种方法都是有效的。我认为模板路线会更惯用 C++,但 OO 路线更适合初学者和非 C++ 程序员。只是两者的混合会出现问题。
  • @jalf:“模板路由会更符合 C++ 的习惯”。我们希望它会,不是吗?然而,在过去的十年中,我只遇到过 一个 了解静态多态性的人。 (其他大多数人可能认为这是一种令人讨厌的性病。)我不敢相信这一切都是因为我在错误的公司工作......
  • 嗯,这取决于你如何定义“惯用语”。这可能不是最常见的路线,但我认为(并希望)这是 C++ 专家推荐的路线。但是,是的,你是对的。
【解决方案3】:

我认为您的问题无需使用模板即可解决。 D 始终是 Date 的派生类,那为什么不只是一个 Date 对象的集合呢?

【讨论】:

  • 其实我主要是对语法很好奇,但是类的一些函数(与问题无关)也需要我有一个确切的类型。
【解决方案4】:

在 C++ 中,这称为概念检查。在 C++ 中,一个常见的最佳实践是继承用于继承接口而不是实现。所以你其实并不关心D是否继承自Date,而是D是否有你需要的Date的接口元素,即它有必要的成员函数等。这就是你不需要让未来的D 类需要从Date 继承,它们只需要实现某些功能。

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

如果你真的想强制 DDate 继承,你可以使用 Boost.StaticAssertBoost.TypeTraits 来检查 D 是否从 Date 继承。

【讨论】:

    【解决方案5】:

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

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

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

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

    如果您想要基类多态,只需将模板排除在外并使用Date*

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

    【讨论】:

      【解决方案6】:

      如果你只想与 Date 对象交互,为什么不直接使用简单的多态性并简单地处理 Date*-s 呢?

      如果您打算在 Calendar 中拥有一个 Dates 集合,其中可以包含不同 Date 子类的实例,那么我怀疑模板是否能正常工作,而且您首先需要多态性来帮助您。

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2012-12-26
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多