【问题标题】:Restricting std::sort to random access iterators将 std::sort 限制为随机访问迭代器
【发布时间】:2011-01-22 16:06:59
【问题描述】:

我只是想知道,既然无论如何你只能将随机访问迭代器传递给std::sort,为什么不首先为随机访问迭代器定义它来强制执行该限制?

#include <iterator>
#include <type_traits>

template <typename ForwardIterator>
typename std::enable_if<
    std::is_same<
        typename std::iterator_traits<ForwardIterator>::iterator_category,
        std::random_access_iterator_tag>::value,
    void>
::type sort(ForwardIterator begin, ForwardIterator end)
{
    // ...
}

我发现单行错误消息比执行过程中由于类型错误导致的页面和页面的错误消息更容易阅读。

您可以对其他算法做同样的事情。标准 C++ 核心语言对于这项任务的表现力已经足够了,对吧?那么,没有这样做的任何特殊原因?

【问题讨论】:

  • 我怀疑:在 C++03 中没有 enable_if,而且通常大多数 TMP 应用程序在标准中包含重要功能的最后期限(本应是 1997 年-是的。
  • @FredOverflow:很好,所以它不存在的事实强烈地向我表明,这个领域没有得到很好的发展。另一种可能性是支持将sort 与实际上是随机访问但低估其类别的迭代器一起使用。如今,您希望人们能够正确地获得迭代器类别,也许在 1997 年并没有那么多,所以当时标准不要求拒绝错误标记的迭代器可能是有用的sort.
  • @Oswald:是的。 iterator_traits 部分专门用于指针。
  • 这肯定只是 QoI 问题吗?该标准指定了一个接口,而不是一个实现;它只需要实现提供与随机访问迭代器一起使用的sort。无论实现是否为双向迭代器提供专门化作为扩展,如果您尝试使用带有sort 的双向迭代器是否提供“改进的”编译错误,或者它是否只是让“自然" 错误消息落空。
  • @James:是的,24.3.1/1 说,“如果Iterator 是迭代器的类型,则需要定义类型(等等)”。我认为这应该被视为标准中其他任何地方使用的“迭代器”一词定义的一部分,所以当std::sort(实际上是算法部分,一般来说,25/4)说它的参数是迭代器时,这意味着用不是一个的东西来调用它是未定义的。

标签: c++ algorithm stl iterator typetraits


【解决方案1】:

核心语言一直具有足够的表达能力来处理此类检查,但是在准备第一个标准时(大约在 1996/1997 年),您可以使用 SFINAE 玩的技巧(enable_if 的基础)并没有尚未为人所知,并且对高级模板向导的支持在编译器中受到限制。

因此,该标准没有强制要求它的原因是因为尚未发明所需的技术。
编译器/库编写者事后没有添加它的原因可能只是简单的经济学:没有足够多的人要求该功能,当人们确实开始要求更好的诊断时,希望在 concepts 提议上照顾它。不幸的是,事实证明这有点太难及时完成了。

【讨论】:

    【解决方案2】:

    我的猜测是,SFINAE 是在标准库实现达到一定成熟度之后发明(或发现)的。在那之后,为了防止引入回归,对核心库的更改必须非常合理,我想单纯的装饰有点难以证明是合理的。

    也就是说,GCC 已经确实对模板相关的错误消息进行了很多诊断,例如执行某种概念检查的宏。例如,GCC-libstdc++ 有以下内容:

    // concept requirements
    __glibcxx_function_requires(_Mutable_RandomAccessIteratorConcept<
       _RandomAccessIterator>)
    __glibcxx_function_requires(_LessThanComparableConcept<_ValueType>)
    __glibcxx_requires_valid_range(__first, __last);
    

    【讨论】:

    • 需要某种#define 来打开这些检查? - 但是,上次我尝试使用这些时,错误消息似乎没有太大改善,如果不是相反的话......
    • @Uncle:我明白了。有趣的。你知道为什么默认关闭吗? bits/concept_check.h 文件中没有记录原因。一条评论表明,概念检查的作者不确定编译器是否足够聪明,可以避免检查的运行时开销。但是,查看代码很明显,编译器 执行此操作。再说一遍,代码是 2000 年的。也许当时编译器的死代码消除真的那么愚蠢。
    • 这里混合了静态和运行时检查,例如“valid_range”测试是在运行时完成的,所以它可以解释为什么它默认被禁用。
    • @Matthieu:你是对的,当然。我完全没有注意到检查 - 可以说 GCC 将这些不同的检查放在一起非常糟糕。
    • 我同意,区分这两个类别是有意义的。
    【解决方案3】:

    实际上,当算法只有一个重载时,您几乎总能通过使用 Boost.ConceptCheck 或 __glibcxx_function_requires 之类的东西在内部引起编译错误来获得更好的诊断。当 SFINAE(enable_if 使用的)给您留下一个空的重载集时,大多数编译器只会告诉您“没有匹配的函数”,这往往不是很有帮助。

    【讨论】:

      【解决方案4】:

      C++ 中模板的优点之一是它们可以有一种静态的“鸭子类型”。我不能说这种特殊情况,但是在许多模板中,只要您保持界面相同,所有层次结构的废话都无关紧要。这是一件好事。

      【讨论】:

      • 这不能回答问题
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-11-14
      • 2015-02-18
      • 2015-12-04
      • 2021-05-22
      • 1970-01-01
      相关资源
      最近更新 更多