std :: remove()可以正常使用文字,但不能使用取消引用的迭代器

时间:2019-12-15 20:33:37

标签: c++ c++17

在学习删除-惯用语的同时,并了解std :: min_element()如何工作How to use std::min_element in C++17?。我想尝试从以下代码中删除最少的元素:

#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);
}

v中有两个最低限度的元素。我试图通过添加诊断程序将它们都删除

int main()
{
    std::vector<int> v{3, 1, 4, 1, 5, 9};

    std::vector<int>::iterator result = std::min_element(v.begin(), v.end());

    v.erase(result); // This removes just one minimum. What if need to remove all?

    v.push_back(1); // Okay, let's add the minimum again

    std::vector<int>::iterator another_result = std::min_element(v.begin(), v.end());

    std::cout << "min element: " << *another_result  << std::endl;

    auto iter = std::remove(std::begin(v), std::end(v), *another_result);
    // If I write 1 instead of *another_result, I manage to remove all 1's. No need to use iter-1 in erase idiom then.

    std::cout << "\nWhere is my iterator pointing? It is at: " << std::distance(v.begin(), iter);

    v.erase(iter, std::end(v)); // All the minimum are gone if I use iter-1 instead of iter and use *another_result

    std::for_each(v.begin(), v.end(), [](const int& x){std::cout << x << " ";}); // Why is still "1" there?
}

link

我的问题是带有注释的代码中突出显示的

  1. 为什么我能够通过提供文字而不是取消引用的迭代器来删除所有的minimum实例?即 为什么下面的工作?
auto iter = std::remove(std::begin(v), std::end(v), 1);

但是

  1. 如果我选择坚持使用取消引用迭代器,则
auto iter = std::remove(std::begin(v), std::end(v), *another_result);

在坚持删除擦除习惯用法时,不会删除所有minimum实例。

2 个答案:

答案 0 :(得分:7)

您似乎正在将参考与向量进行比较。然后,您传入的元素将移动remove,并在与之进行第二次比较时,引用将观察到其他值。

这很好用:

int by_value = *another_result;
auto iter = std::remove(std::begin(v), std::end(v), by_value);

您正在使用的std::remove重载的第三个参数采用const T&,但它在执行操作的过程中“使引用无效”。

如果您查看en.cppreference上的“可能的实现”

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;
}

此问题在“注释”部分中也提到为:

  

由于std :: remove通过引用获取值,因此可能会出现意外情况   行为,如果它是对[first,   最后)。

答案 1 :(得分:1)

如果您要一次性删除所有最小值,可以执行以下操作:

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() );
}

请注意,min_val首先是副本(请参阅PeterT的答案以获取解释)。上面的内容可能可以修改为与其他容器一起使用。

请记住,std::remove并没有真正删除任何内容。该函数的返回值将指向新的 last元素所在的位置之后,然后从此处调用容器的erase方法以删除该位置上的所有元素。