【问题标题】:Why does std::sub_match<T> publicly inherit from std::pair<T, T>?为什么 std::sub_match<T> 公开继承自 std::pair<T, T>?
【发布时间】:2011-12-16 14:24:18
【问题描述】:

我正在阅读std::sub_match&lt;BidirectionalIterator&gt; 的文档,发现它公开继承自std::pair&lt;BidirectionalIterator, BidirectionalIterator&gt;。既然sub_match只是一个字符序列的一对迭代器,加上一些额外的功能,我可以理解它是用pair实现的,但是为什么要使用公共继承呢?

从std::pair&lt;T,U&gt; 公开继承的问题与从大多数其他标准类公开继承的问题相同:它们不打算进行多态操作(特别是它们没有定义虚拟析构函数)。其他成员也将无法正常工作,即赋值运算符和交换成员函数(它们不会复制sub_match 的matched 成员)。

为什么 Boost 开发人员和委员会决定通过从 pair 公开继承而不是使用组合来实现 sub_match(或者如果他们想通过 first 和 second 保持成员访问权限,则使用声明的私有继承) )?

【问题讨论】:

    标签: c++ regex boost c++11


    【解决方案1】:

    这是一个有趣的问题。据推测,他们认为这是安全的 因为无论如何没有人会动态分配一个。有关 您将获得 sub_match 对象的唯一方法是作为返回值 来自basic_regex 的某些功能,或作为其他功能的副本 sub_match,所有这些都将是临时的或本地的 变量。

    请注意,无论如何保留sub_match 对象并不安全,因为 它们包含迭代器,其生命周期...似乎没有在 标准。直到match_results 对象被重用?直到 string 对填入match_results 的函数的操作数 对象被破坏?还是?

    我仍然会避免公共继承。但在这种情况下,它 不像看起来那么危险,因为你真的没有理由 曾经想要动态分配sub_match。

    【讨论】:

    • 我同意可能永远不会发生的动态分配。但是,= 和 swap 仍可能出现问题。例如,我在考虑 Boost.Range,但它不需要范围是可分配或可交换的。然而,有趣的是,Boost.Range 算法不接受sub_matches 作为参数,但如果它们是通过对pair 的引用来操作的(特征类问题)。
    【解决方案2】:

    因为 C++ 无法在没有公共继承的情况下继承接口。您可以使用私有继承来继承实现,但是一切都是私有的。如果您想要与std::pair 相同的界面,您必须成为std::pair。

    另外,考虑一下。这显然是未定义的行为:

    std::sub_match<BidirectionalIterator> theMatch = ...;
    std::pair<BidirectionalIterator> *pMatch = &theMatch;
    delete pMatch;
    

    但这是这样的:

    std::sub_match<BidirectionalIterator> theMatch = ...;
    std::pair<BidirectionalIterator> *pMatch = &theMatch.pair;
    delete pMatch;
    

    为什么第一个比第二个更令人担忧?

    sub_match 和pair 是轻量级对象(当然取决于它们的内容)。它们旨在通过引用进行复制或传递,所有这些都是 100% 安全的。几乎没有理由在堆上分配它们并通过指针使用它们。因此,虽然我理解您的担忧,但我认为这不太可能发生在任何实际代码中。

    【讨论】:

    • 确实,您的第二个示例(几乎)与第一个示例一样有缺陷,但这是因为您认为 pair 成员将是公开的:您为什么会假设如此糟糕的封装会是用过的?虽然我同意删除可能永远不会成为问题,但我不同意通过引用传递是 100% 安全的,因为其他非虚拟成员函数。
    • 您可以使用私有继承继承(部分)公共接口:class sub_match : pair { using pair.first; using pair.second; bool matched; }
    • 如果您想要与std::pair 相同的接口而不是一个,您可以使用私有继承以及使用声明和/或委托。当然,这对子类的开发者来说是更多的工作。
    【解决方案3】:

    regex 的作者对此不得不说:http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2003/n1429.htm#matches_discussion

    恐怕没有很具体的问题。

    我猜这个决定是在重新发明轮子和引入一点误用风险之间进行权衡。请注意,一般不需要构造sub_match,它们是从regex 函数返回的。此外,成对的迭代器是实现范围的一种非常实用的方式。

    【讨论】:

      【解决方案4】:

      因为他们不需要虚拟析构函数? ;-)

      【讨论】:

      • 析构函数不是唯一无法正常工作的成员:赋值运算符和交换都将仅复制 pair 成员而不是 sub_match 包含的成员(特别是 matched 布尔值) .
      • 作者显然认为他们不需要虚拟析构函数,或者他们会提供一个。但是为什么,因为在大多数情况下,公共继承意味着在基类中需要一个虚拟析构函数。
      • @LucTouraille 好点。很明显,sub_match 没有意图是 A pair。
      • @JamesKanze:你说得对,“is-a”关系不适用于这两个类,不遵守 Liskov 替换原则。
      • @Luc Touraille:虽然期望破坏对多态对象正常工作是非常合理的,但我想说,期望赋值或交换在多态上下文中做任何明智的事情都是自找麻烦。
      【解决方案5】:

      如果std::sub_match&lt;BidirectionalIterator&gt; 没有自己的状态,那么它可以从std::pair 继承。不过不要在家里做。

      【讨论】:

      • 状态与它无关。如果您通过pair&lt;&gt; 指针删除sub_match 对象,这是未定义的行为。
      • 此外,它确实有自己的状态(一个布尔值,表示匹配是否成功)。
      猜你喜欢
      • 1970-01-01
      • 2015-07-23
      • 2023-02-16
      • 1970-01-01
      • 2017-11-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多