C++ std::remove()可以按预期使用文本,但不能使用取消引用的迭代器

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:

在学习这个习惯用法的同时,还要了解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::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() );
    }