C++ std::remove()可以按预期使用文本,但不能使用取消引用的迭代器
在学习这个习惯用法的同时,还要了解std::min_element()是如何工作的。我想尝试从以下代码段中删除最小元素:C++ std::remove()可以按预期使用文本,但不能使用取消引用的迭代器,c++,c++17,C++,C++17,在学习这个习惯用法的同时,还要了解std::min_element()是如何工作的。我想尝试从以下代码段中删除最小元素: #include <algorithm> #include <iostream> #include <vector> int main() { std::vector<int> v{3, 1, 4, 1, 5, 9}; std::vector<int>::iterator result = std:
#include <algorithm>
#include <iostream>
#include <vector>
int main()
{
std::vector<int> v{3, 1, 4, 1, 5, 9};
std::vector<int>::iterator result = std::min_element(v.begin(), v.end());
std::cout << "min element at: " << std::distance(v.begin(), result);
}
但是,
在坚持删除擦除惯用法时,不会删除所有minimum实例。看起来您正在与向量中的引用进行比较。然后,您传入的元素会被
remove
移动,并且在第二次与它进行比较时,引用会观察到一些其他值
这很好:
int by_value = *another_result;
auto iter = std::remove(std::begin(v), std::end(v), by_value);
您正在使用的std::remove
重载的第三个参数采用常量&
,但它在执行其操作的过程中使引用“无效”
如果您查看上的“可能的实现”
template
ForwardIt移除(ForwardIt first、ForwardIt last、const T和value)
{
first=std::find(第一个、最后一个、值);
如果(第一个!=最后一个)
for(ForwardIt i=first;++i!=last;)
如果(!(*i==值))
*first++=std::move(*i);//这里它更改了“value”指向的值
//如果使用向量内元素的引用
先返回;
}
“注意事项”一节中也提到了这个问题:
因为std::remove通过引用获取值,所以它可能具有意外的
行为,如果它是对范围[first,
最后)
如果您想一次性删除所有最小值,您可以做一些更奇怪的事情,例如:
template<class T>
void remove_min( std::vector<T> &container ) {
if ( container.empty() ) return;
T min_val = *std::min_element( container.begin(), container.end() );
container.erase( std::remove( container.begin(), container.end(), min_val ), container.end() );
}
模板
void remove_min(标准::向量和容器){
if(container.empty())返回;
T min_val=*std::min_元素(container.begin(),container.end());
container.erase(std::remove(container.begin()、container.end()、min_val)、container.end());
}
请注意,min_val
首先是一个副本(请参见PeterT的答案进行解释)。上述内容可能会被修改,以便与其他容器一起使用
请记住,
std::remove
并不真正删除任何内容。函数的返回值将指向新的最后一个元素所在的位置,然后从该位置调用容器的erase
方法,从该位置删除所有元素。这里有很多内容需要阅读,而且看起来不像是一个非常简单的代码这是一个供将来使用的问题。你能把你的问题集中一点吗?remove-erase习惯用法只对容器使用一些简单的操作。如果你解除迭代器的限制,它就不再与容器有任何关系了。(我知道,这不是一个真正的答案,但希望它能让你找到正确的思路。)删除后,要删除的部分不保证有任何特定的值。LRwM:缩短文本。Kenny:我不想键入;1、2或3,不管是通用代码中的最小值。问题是,我传递*另一个结果是否有错?答案是“是”,但我不知道不这样做还能放在那里什么一些非泛型的工作。Eljay,是的,我知道这一部分。我担心迭代器没有指向要擦除部分的正确位置,除非我在本例中传递一个文本。感谢PeterT,我意识到了这一微妙的事情,因为我刚刚休息了一会儿,正在思考const T&value是否出了问题:=*另一个_值看起来是这样的。如果某个东西与literal一起工作,那么它也应该与命名变量一起工作,更好地与const lvalue reference一起工作。我甚至尝试了UnaryPredicate p。但是,正是隐藏的移动让我受益匪浅。经验教训。非常感谢。请注意list
和forward\u list中的成员版本
没有这个问题。@Sowmya,常量t&value
只说明此函数(删除
)不会(直接)修改值,它并不是说其他人不能修改该值。在这种情况下,修改是由迭代器值完成的,这些值不是const_iterator
s,只是iterator
s,它们确实会更改值。这很微妙…@ChrissMM,是的,我意识到我刚才说的是多么可怕和愚蠢的事情,并删除了我以前的同事mment没有看你的回复。然而,即使是我第一次评论,你的回复仍然是有意义的。我感到难过的是,T的持续性并没有阻止我做愚蠢的事情,它仍然移动了一个本应被转换为常数的值,因此没有移动。这样说有点误导“引用观察到一些其他值”。它已无效;它没有观察到任何东西;使用它有未定义的行为。直到对涉及移动操作的任何内容都要格外小心和偏执。例如,我仍然可能犯下一个可怕的错误,即“模板void remove_min(std::vector&container){if(container.empty())return;container.erase(std::remove(container.begin(),container.end(),*std::min_元素(container.begin(),container.end()),container.end();)“``注释中只有一个倒勾;没有多行代码。;)在您刚刚粘贴的内容中,它也有同样的问题,因为迭代器指向的内容将发生变化。我发现,任何与迭代器相关的内容,您都必须小心:pIt说“迭代器指向的内容将发生变化”有点误导"。它已经无效;它没有指向任何东西;使用它有未定义的行为。@LightnessRaceswithMonica,我不相信这是真的。我不相信std::remove
会使迭代器无效,因为容器本身从不更改,其中的项只是移动。例如,*itr=5
不会更改vaitr的有效性,仅仅是它的内容。诚然,标准对有效性的唯一说明是
auto iter = std::remove(std::begin(v), std::end(v), *another_result);
int by_value = *another_result;
auto iter = std::remove(std::begin(v), std::end(v), by_value);
template< class ForwardIt, class T >
ForwardIt remove(ForwardIt first, ForwardIt last, const T& value)
{
first = std::find(first, last, value);
if (first != last)
for(ForwardIt i = first; ++i != last; )
if (!(*i == value))
*first++ = std::move(*i); //here it changes the value that "value" points to
//if you are using a reference of an element inside the vector
return first;
}
template<class T>
void remove_min( std::vector<T> &container ) {
if ( container.empty() ) return;
T min_val = *std::min_element( container.begin(), container.end() );
container.erase( std::remove( container.begin(), container.end(), min_val ), container.end() );
}