通过在C ++中直接访问其迭代器来删除容器的元素

时间:2016-03-02 11:54:44

标签: c++ vector iterator containers

我在main函数中声明了一个std::vector<int>,并希望从中删除所有偶数元素,但只能将它的迭代器传递给一个名为remove_even的函数,它接受一个开始并结束容器的迭代器。

#include <iostream>
#include <algorithm>
#include <vector>

void remove_even(auto start, auto end) {
    while(start != end) {
        if(*start % 2 == 0)
        // Remove element from container
    }
}

int main() {
    std::vector<int> vec = {2, 4, 5, 6, 7};
    remove_even(vec.begin(), vec.end());
}

有没有办法在C ++中执行此操作,还是我必须直接将向量传递给函数?

5 个答案:

答案 0 :(得分:5)

std::vector本身具有允许删除向量中所需元素的方法erase

使用迭代器所能做的就是调用标准算法std::remove_if,然后在方法erase的调用中使用返回的迭代器。

例如

#include <iostream>
#include <vector>
#include <algorithm>

std::vector<int>::iterator remove_even( std::vector<int>::iterator first,
                                        std::vector<int>::iterator last )
{
    return std::remove_if( first, last, []( int x ) { return x % 2 == 0; } );
}

int main()
{
    std::vector<int> vec = { 2, 4, 5, 6, 7 };

    for ( int x : vec ) std::cout << x << ' ';
    std::cout << std::endl;

    vec.erase( remove_even( vec.begin(), vec.end() ), vec.end() );

    for ( int x : vec ) std::cout << x << ' ';
    std::cout << std::endl;
}    

程序输出

2 4 5 6 7 
5 7 

答案 1 :(得分:1)

这是IMO的正确方法:

template <typename T>
T remove_even(T start, T end) {
   return std::remove_if(start,end,[](const auto& item){return item%2==0;};
}

int main() {
    std::vector<int> vec = {2, 4, 5, 6, 7};
    vec.erase(remove_even(vec.begin(), vec.end()),vec.end());
}

答案 2 :(得分:1)

来自cplusplus.com:

  

迭代器是指向一系列元素(例如数组或容器)中的某个元素的任何对象,它能够使用一组运算符迭代该范围的元素(至少具有increment(++)和dereference(*)operator)。

如引言中所述,它指向一系列元素中的元素。它不需要提供有关其所在范围的任何信息,即关于存储元素的容器的信息。

正如评论中所述,从向量(或任何其他容器)中删除元素是影响容器的操作,而不仅仅是对象。因此,您必须始终在容器上调用erase()或类似功能

你所询问的是(有些,不完全)与此相似:

void remove_or_not(int& i){
    //do something with i to remove it from a container
    //but we dont have a container here
}

int main(){
    std::vector<int> vec;
    //fill vec and generate some int n
    remove_or_not(vec[n]);
}

在上面的例子中调用remove_or_not()时,我们只是传递对int的引用 - 我们完全丢失了它在容器中的信息,所以很明显我们无法从任何容器中删除它。 / p>

当使用迭代器尝试相同时,我们仍然知道元素在容器内的信息 - 但是我们可能丢失了容器中的信息,因为迭代器不需要保存这些信息。
例如。 C样式数组上的迭代器可以只是一个指针。我们可以递增和递减它,并将它与指向第一个元素的指针和最后一个元素后面的指针进行比较。但是根本不需要知道关于数组大小或数组的任何信息。

PS:对于如何正确实施所需内容的方法,请参阅已发布的答案,我认为不需要重复这些答案。

答案 3 :(得分:0)

通过将迭代器传递给函数,无法做到这一点。您必须将整个向量传递给函数,如下所示:

void remove_even(std::vector<int> v)
{
    for (auto it = v.begin(); it != v.end();) {
        if ((*it) % 2 == 0) {
            it = v.erase(it);
        } else {
            ++it;
        }
    }
}

答案 4 :(得分:0)

您可能需要考虑通过x值引用传递容器的习惯用法,然后返回复制/移动版本。

这提供了效率的优势以及呼叫站点的可读性。

下面的扩展示例。

请注意,所有&#39;明显&#39;编译器(RVO)将省略(优化掉)矢量的复制。

另请注意,由于Container&&是在推断的上下文中进行评估的,因此它将自动为const Container&Container&&,以最适合呼叫网站的上下文为准。这意味着可以使用相同的函数来改变现有容器(x = sorted(x))或制作变异副本(x = sorted(y)

#include <iostream>
#include <vector>
#include <algorithm>

template<class Container, class Predicate>
auto all_except(Container&& c, Predicate&& pred)
{
    auto result = std::forward<Container>(c);

    result.erase(std::remove_if(std::begin(result),
                                std::end(result),
                                std::forward<Predicate>(pred)),
                 std::end(result));
    return result;
}

template<class Container>
auto sorted(Container&& c)
{
    auto result = std::forward<Container>(c);
    std::sort(std::begin(result), std::end(result));
    return result;
}

auto even_items = [](auto const& item) { return item % 2 == 0; };
auto odd_items = [](auto const& item) { return item % 2 != 0; };


template<class Vec>
void emit(const Vec& v)
{
    std::cout << "[";
    auto sep = " ";
    for (auto const& e : v) {
        std::cout << sep << e;
        sep = ", ";
    }
    std::cout << " ]" << std::endl;
}

int main()
{
    std::vector<int> vec = {65, 2, 32, 63, 9, 13, 88, 22, 4, 5, 6, 7};

    emit(all_except(vec, even_items));
    emit(all_except(vec, odd_items));

    emit(all_except(sorted(vec), odd_items));

    vec = sorted(all_except(std::move(vec), even_items));
    emit(vec);

    return 0;
}

预期结果:

[ 65, 63, 9, 13, 5, 7 ]
[ 2, 32, 88, 22, 4, 6 ]
[ 2, 4, 6, 22, 32, 88 ]
[ 5, 7, 9, 13, 63, 65 ]