在空容器上调用 std::partition 的行为?

Posted

技术标签:

【中文标题】在空容器上调用 std::partition 的行为?【英文标题】:Behaviour of std::partition when called on empty container? 【发布时间】:2009-12-14 13:27:35 【问题描述】:

在空容器 (std::list) 上调用 std::partition 时遇到问题。

std::list<int>::iterator end_it = std::partition(l.begin(), l.end(), SomeFunctor(42));
std::list<int>::iterator it = l.begin();

while (it != end_it)

  // do stuff

如果列表为空,std::partition 返回一个不等于的迭代器 到 l.end()。这是默认行为吗?

【问题讨论】:

我无法重现该问题。即使是空容器,它对我来说也能正常工作......这里是l.begin() == l.end(),但这是一个特殊情况。 【参考方案1】:

我是否遗漏了什么,或者不应该:

std::list<int>::iterator end_it = l.begin();

是:

std::list<int>::iterator end_it = l.end();

但实际上我认为 partition() 的返回值对于一组空值是未定义的。返回值定义为:

一个迭代器 i 使得对于任何 迭代器 j 在 [first, i) 范围内, pred(*j) != false,对于任何 [i, last) 范围内的迭代器 k, pred(*j) == false。

恕我直言,谓词不能应用于结束迭代器,因为它不可取消引用。

【讨论】:

这看起来确实像 OP 代码中的一个错误,但如果列表像他所说的那样是空的,那么无论如何都没关系,所以可能不是正确的答案。 我认为在这种情况下定义了返回值。它应该等于first。等于last。这个迭代器值满足要求的条件:两半都是空的,因为两个半开范围[first,i)[i,last) 都是空的。 YUp:原因是存在范围 [first, last) 为空。因此,类似“对于范围 [first, i) pred(j)!= false 中的任何迭代器 j 的声明都成立,因为根本没有迭代器 j,因此没有 pred(j)==false 的迭代器。【参考方案2】:

目前,确实没有好的答案。 C++ 委员会问题编号1205 的库工作组正好涵盖了这个问题。该问题包括主要和替代提议的解决方案,但尚未被接受或拒绝(我不喜欢任何一个)。

由于标准没有明确定义将算法应用于空范围的结果,我想说这样做的结果目前是未定义的。我认为有希望在下一版本的标准中定义它,但目前还没有。即使是这样,从实际的角度来看,至少在一段时间内避免它可能会更好,因为编译器可能需要一段时间才能在这方面符合要求(尽管它通常应该是相当 轻松修复)。

编辑,主要是为了回应 Martin B 的评论:为 [alg.partitions] 列出了该问题,其中包括 std::partitionstd::stable_partition。不幸的是,提议的措辞似乎直接针对任何一个。它引用的段落(至少在 N2960 中)在 std::is_paritioned 的描述中,这与 Martin B 所描述的差不多,尽管他使用了错误的名称。更糟糕的是,提议的主要解决方案是非规范性注释的形式。

不过,正如我所说,我真的不喜欢这两个提议的解决方案。主要尝试将要求放在非规范性注释中。如果它们阐明了其他地方确实已经存在但可能很难找到的需求,那么这些注释很好。在这种情况下,我很确定要求确实不存在。替代解决方案更好,但未能解决空范围是否为有效范围的核心问题。

IMO,更好的解决方案将从 §24.1/7 开始。这已经告诉我们:“范围 [i, i) 是一个空范围;...”我认为它应该添加规范性语言来明确说明空范围是或不是有效范围。如果它不是有效范围,则无需添加任何其他内容 - 已经明确将算法应用于无效范围会产生未定义的行为。

如果空范围有效,则需要添加规范措辞来定义将每个算法应用于空范围的结果。这将回答基本问题,然后说明该答案对每个特定算法的含义。

【讨论】:

第 1205 期的链接在这里:open-std.org/jtc1/sc22/wg21/docs/lwg-active.html#1205 我在这里找到的对分区的唯一引用是 std::partitions() (注意末尾的 S),我想这是测试特定序列是否按值分区。你有专门引用 std::partition() 的链接吗?【参考方案3】:

This page 显示std::partition 行为的代码。

【讨论】:

【参考方案4】:

不,std::partition 应该返回结束迭代器,对我 (gcc-4.4.2) 来说是这样。

我认为你在某个地方有错误。在您的代码或编译器中。

【讨论】:

【参考方案5】:

声明partition的前提条件是[first, last)应该是一个有效的范围SGI says:

如果 i 和 j 都是有效的迭代器,并且 j 可以从 i [2] 到达,则范围 [i,j) 是有效范围。

这样就可以在空范围内使用partition

当然,sgi 不是标准。但它非常接近:)

【讨论】:

在这种情况下可能不是。该标准似乎没有为 partition() 指定要求或先决条件。或者我错过了:-) 应该在某处声明partition() 的迭代器参数代表一个范围。如果没有,则需要 DR。

以上是关于在空容器上调用 std::partition 的行为?的主要内容,如果未能解决你的问题,请参考以下文章

为forward_list实现stable_partition

NullPointerException:尝试在空对象引用上调用虚拟方法 findViewById(int)'

NullPointerException 尝试在空对象引用上调用 method.GoogleMap.setMapType(int)'

致命异常: main ,尝试在空对象引用上调用虚拟方法 [重复]

在空 WORDPRESS 上调用成员函数 insert()

SearchView 尝试在空引用上调用虚方法