我参考了很多资源,但无法理解有关它如何准确修改容器中元素的部分。 我已经写了一些代码来试图理解,谁能解释一下发生了什么?
#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
答案 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::remove
和 std::remove_if
等算法适用于序列,即由一对迭代器指定的元素。第一个迭代器指向序列的第一个元素,第二个迭代器指向元素序列的末尾。实际上,算法移动或复制序列中后面的元素以覆盖正在删除的元素。
请注意,上一段没有使用容器这个词。容器是管理元素序列的一种方式,但不是唯一的方式。算法对容器一无所知。 remove
和 remove_if
无法更改他们一无所知的容器的大小。
在将元素传递给 remove
或 remove_if
之后查看容器时看到的是修改后的序列(从容器的开头到迭代器所指向的元素)算法返回),然后是剩下的任何东西。对于像 char
这样的简单类型,这个残基可能是一开始就存在的。对于具有非平凡移动赋值运算符的更复杂类型,剩余部分由移动赋值留下的任何内容组成。对于 C++ 标准库中定义的类型,这意味着剩余部分由处于未指定但有效状态的对象组成。对于在标准库以外的其他地方定义的类型,它们将处于设计者选择的任何状态。