我可以滥用谓词在 remove_if 移除元素之前对它们执行操作吗?

5
我有一个对象的std::list。该列表已排序,必须保持不变。我需要找到那些满足某个条件(我有一个谓词)的对象,将它们传递给一个函数,然后从列表中删除这些对象。
编写循环调用std::find_if(),对其结果(如果有)执行操作,调用list.erase(),并将其结果作为begin迭代器传递给下一个std::find_if()的调用并不太难。然而,人们通常发现这样的代码比编写它更难读懂。
因此,我更愿意使用一些std lib中的算法,而不是编写自己的循环。
一个想法是(滥用)std::list<>::remove_if():在谓词匹配的元素上调用操作,直到返回true,以便列表将删除元素。这符合标准吗?(项目本身不会改变,只有它们引用的数据。)
或者你能想出更好的解决方案吗?(再次强调,主要目标是使其易于阅读和理解。)也许是因为我刚遇到这个问题,但对我来说,这似乎不是对象序列的一个不常见的使用模式。
注:暂时我们还停留在C++03领域。:-/ C++11/14/17的解决方案很有趣,因此欢迎,但我确实需要一些可以在C++03中使用的东西。

你可以使用 for_each,在执行期间将迭代器存储在某个地方,并在最后删除这些元素。 - skypjack
@skypjack:我也考虑过这个方法。不过它看起来很笨拙,而且需要对列表进行两次遍历。(请注意,该列表受互斥锁保护,在此过程中阻止其他操作。) - sbi
列表进行一次遍历,迭代器进行一次遍历(可能有几个)。无论如何,我明白你的意思,但我觉得在一元谓词中执行操作的想法有些棘手,这个一元谓词最好只用于检查条件。 - skypjack
如果您有迭代器,您就不必遍历列表来删除它们。这具有恒定的复杂度。此外,删除一个迭代器不会使其他迭代器失效。我们说的是 std::list,对吗? - skypjack
@skypjack:啊,我错了。你写的“删除那些元素在结尾处”我读成了“调用remove_if()” - 显然你没有写。不过,我刚刚遇到了std::stable_partition(),看起来正是我需要的东西。 - sbi
显示剩余2条评论
1个回答

5

我可以滥用谓词在remove_if删除元素之前进行操作吗?

可以。标准规范中没有要求谓词必须是纯函数。因此,这个C++11解决方案完全可行:

my_list.remove_if([f, pred](Elem const& e){
    if (pred(e)) {
        f(e);
        return true;
    }
    return false;
});

甚至没有要求谓词必须返回true。你甚至可以使用remove_if作为一个“穷人版”的、不必要混淆的for_each:

my_list.remove_if([f](Elem const& e){
    f(e);
    return false;
});

这样做是没有意义且效率低下的,但它确实符合标准。

你可以将其写成一个函数对象,相当于在C++03中。无论你是否认为这比for循环更易读,这都是一种观点。但它并不是错误的。


1
我很少发现有人滥用预期的API和一般期望“不错”。否则,代码就会出现许多小问题。这可能不违反规范;就这样吧。 - user2864740
最终,我选择了std::stable_partition,它可以很好地将我要查找的项放在列表的两端,这样我就可以处理和删除它们。尽管如此,它确实回答了我的原始问题,所以我会选择它。 - sbi

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接