std::forward_list::remove_if 谓词的要求

Requirements on std::forward_list::remove_if predicates

考虑这段代码:

struct T
{
bool status;
UsefulData data;
};

std::forward_list<T> lst;

lst.remove_if([](T &x) -> bool { return x.status= !x.status; });

即一次性切换状态和删除非活动元素。

根据cppreference,上面的代码似乎是未定义的行为(强调我的):

template< class UnaryPredicate >
void remove_if( UnaryPredicate p );

p - unary predicate which returns true if the element should be removed. The signature of the predicate function should be equivalent to the following:

bool pred(const Type &a);

The signature does not need to have const &, but the function must not modify the objects passed to it. The type Type must be such that an object of type forward_list<T,Allocator>::const_iterator can be dereferenced and then implicitly converted to Type.

不过,目前的工作草案似乎限制较少(N4659 [forwardlist.ops]):

void remove(const T& value)
template <class Predicate> void remove_if(Predicate pred);

Effects:

Erases all the elements in the list referred by a list iterator i for which the following conditions hold: *i == value (for remove()), pred(*i) is true (for remove_if()). Invalidates only the iterators and references to the erased elements.

Throws:

Nothing unless an exception is thrown by the equality comparison or the predicate.

Remarks:

Stable (20.5.5.7).

Complexity:

Exactly distance(begin(), end()) applications of the corresponding predicate.

标准的其他部分对谓词有额外的限制吗?

我已经在许多编译器上测试了上面的代码,它编译并且似乎按预期工作。我真的需要遍历列表两次吗?

根据你问题下的评论,我的看法是:

  • forward_list::remove_if 不是算法,因此不属于 [algorithms.requirements].

    的规则
  • [forwardlist.ops] 不限制您在谓词中可以做什么。

  • forward_list::remove_if 需要对范围内的每个项目恰好应用谓词一次。

因此,在这种情况下修改谓词中的对象是完全合法的,如果有点反社会的话。

编辑:

根据T.C.后来的回答,长期不合法...

委员会的图书馆工作组刚刚将 LWG issue 2998 移动到 "Ready" 状态,基本上确认委员会的意图是第 28 条(算法条款)的相关要求适用于类似的列表操作。这包括 Predicate 不应用任何 non-constant 函数的要求。

一旦通过问题决议(应该在下一次委员会会议上发生),OP 中的代码将正式具有未定义的行为。尽管如此,它通常应该按预期工作,除非有意的敌对实施。如果需要具有保证 well-defined 行为的实现,使用迭代器和 erase_after.

手动编写一个并不难