remove_if() 究竟是如何工作的?

时间:2021-05-26 17:37:41

标签: c++ stl c++17

我参考了很多资源,但无法理解有关它如何准确修改容器中元素的部分。 我已经写了一些代码来试图理解,谁能解释一下发生了什么?

#include<bits/stdc++.h>
using namespace std;

bool test(char c)
{
    return (c=='a');
}

int isPalindrome(string A) 
{
    remove_if(A.begin(), A.end(), test);
    cout<<A<<'\n';   
 }

int main() 
{
    
    string A="aaaaabbbbb";
    isPalindrome(A); 

    return 0;
}

根据我目前的理解,它应该将所有 a 字符移动到 b 的右侧,如果我们尝试打印它,我们应该得到

bbbbbaaaaa

相反,我得到了

bbbbbbbbbb

5 个答案:

答案 0 :(得分:5)

std::remove_if,不是std::move_if。它只是删除所有从 test 函数返回 true 的元素。现在,为什么会有额外的 b?好吧,你不应该看到他们。 std::remove_if 返回一个标记序列新结尾的迭代器,您应该使用返回值相应地调整容器的大小。因此,假设我们有一个字符串 "Hello, World!",我们需要从中删除所有 l。下面是一个示例程序,用于展示执行此操作的一种方法:

#include <algorithm>
#include <string>
#include <iostream>

int main()
{
    std::string a = "Hello, World!";
    a.resize(std::distance(
        a.begin(),
        std::remove_if(a.begin(), a.end(), [](char c){return c == 'l';})
    ));
    std::cout << a << '\n';
}

输出:

Heo, Word!

答案 1 :(得分:3)

<块引用>

根据我目前的理解,它应该将所有 a 字符移到 b 的右侧

差不多。它做相反的事情:它(有点)将 b 字符移动到 a 的左侧。更准确地说,它在删除的元素上“移动分配”。

因此,左边的元素是从右边移动的b(在左边的a之上),右边的元素是被移动的b向左。由于移动字符与复制相同,即它不会修改从中移动的参数,这使得左右元素都是 b。

由于该算法的预期用例是删除 a,因此容器中没有剩余 a 不是问题。请注意,对于不同的输入,一些 a 可能会保留在容器的右端。右边的元素要么是向左移动的元素的剩余部分(不是 a's),要么是因为要被移除而没有移动的元素(a's)。

附言您现在可以在 C++20(或将来,如果您已标记 C++17)中使用:

std::erase_if(A, test);

答案 2 :(得分:2)

您对 remove_if 的工作方式是正确的。但是,它返回一个迭代器,该迭代器指向已删除元素中的第一个元素,该迭代器与未删除元素的最后一个元素相同。然后您需要删除被删除的元素以获得您想要的答案。因此,在函数isPalindrome 中,将您的 remove_if 行替换为

   const auto new_end = remove_if(A.begin(), A.end(), test);
   A.erase(new_end, A.end());

然后你会得到 bbbbb

这种调用 remove_if 后跟 erase 的模式经常发生。这两行通常合二为一,这就是所谓的“remove-erase idiom”。在您的情况下,将您的 remove_if 行替换为

   A.erase( remove_if(A.begin(), A.end(), test), A.end() );

答案 3 :(得分:1)

如果需要,该算法只是将等于 'b' 的元素移动或复制(取决于使用的迭代器)到存储等于 'a' 的元素的位置。

它不会交换容器的元素。

答案 4 :(得分:1)

我在几条评论中提出了同样的观点,但我认为需要更详细地解决这个问题。

std::removestd::remove_if 等算法适用于序列,即由一对迭代器指定的元素。第一个迭代器指向序列的第一个元素,第二个迭代器指向元素序列的末尾。实际上,算法移动或复制序列中后面的元素以覆盖正在删除的元素。

请注意,上一段没有使用容器这个词。容器是管理元素序列的一种方式,但不是唯一的方式。算法对容器一无所知。 removeremove_if 无法更改他们一无所知的容器的大小。

在将元素传递给 removeremove_if 之后查看容器时看到的是修改后的序列(从容器的开头到迭代器所指向的元素)算法返回),然后是剩下的任何东西。对于像 char 这样的简单类型,这个残基可能是一开始就存在的。对于具有非平凡移动赋值运算符的更复杂类型,剩余部分由移动赋值留下的任何内容组成。对于 C++ 标准库中定义的类型,这意味着剩余部分由处于未指定但有效状态的对象组成。对于在标准库以外的其他地方定义的类型,它们将处于设计者选择的任何状态。

相关问题