std :: erase()非引用与引用的行为

时间:2016-08-22 04:07:28

标签: c++ c++17

假设我有一个类型为my_object的向量,其大小为3,我想从我的向量中获取3个元素,将它们存储在引用中

然后我想使用std::remove_if() 使用std::remove和element_1以及element_2删除和删除element_3

以下是my_object

  class my_object {
  public:
     my_object(int num);
     bool exists() const;
  private:
     int num;
  };
  my_object::my_object(int num) : num(num) {}
  bool my_object::exists() { return num == 1; }

以下是main

  std::vector<my_object> my_vector;
  int main() {
     my_object e1(2);
     my_object e2(2);
     my_object e3(1); // i.e exists() will return true in lambda

     my_vector.push_back(e1);
     my_vector.push_back(e2);
     my_vector.push_back(e3);   

     const auto& element_1 = my_vector.at(0);
     const auto& element_2 = my_vector.at(1);
     const auto& element_3 = my_vector.at(2);

     auto lambda = [](auto& src) { return src.exists() };
     std::erase(std::remove_if(b, e, lambda), e); // remove_if for element_3
     std::erase(std::remove(b, e, element_1), e);
     std::erase(std::remove(b, e, element_2), e);   
     return 0;
  }

非常奇怪的是,当我通过引用声明element_1,element_2,element_3而不是擦除没有正确完成并且大小不会减小到0,但是当我写const auto时没有&然后它完全正常,任何人都可以向我解释这种奇怪的行为吗?

1 个答案:

答案 0 :(得分:2)

折扣擦除方法,这些引用就是:对容器中的对象的引用。一旦removeremove_if在进行序列的过程中执行了他们的任务移动分配,那些引用仍然指的是相同的元素,但这些插槽的占用者是:

  • 充其量,仍然是有效的对象,因为有效的对象要么停留在原来的位置,要么在那里移动分配。
  • 一些以前的自我的贝壳,因为引用现在指的是一个从未被目标移动回收的源对象。

我不会潜入std::remove,而是。看看std::remove_if

这个相当简单的例子
#include <iostream>
#include <vector>
#include <algorithm>

int main()
{
    std::vector<int> v = { 1,2,3,4,5 };
    const auto& a1 = v.at(0);
    const auto& a2 = v.at(2);
    const auto& a3 = v.at(4);

    std::cout << a1 << ' ' << a2 << ' ' << a3 << '\n';

    std::remove_if(v.begin(), v.end(), [](const auto& x) { return x == 3; });

    std::cout << a1 << ' ' << a2 << ' ' << a3 << '\n';
}

<强>输出

1 3 5
1 4 5

正如您所看到的,std::remove_if的功能描述符合您在代码中看到的内容。已移除3元素,并将4元素移动分配到其位置。你在这里看不到的是5元素被移动分配到4的位置,你现在看到的5值恰好来自插槽其中5 。该标准表示该对象是“有效的”,但具有“未指定”的值。我们可以通过确保移动来源的移动来源实际上是“无效的”(据我们所关注)来验证。修改我们的原始程序给了我们这个:

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

struct S
{
    S(int n) : value(n), isvalid(true)
    {
    }

    S(const S& s) : value(s.value), isvalid(true)
    {
    }

    S(S&& s) : value(s.value), isvalid(true)
    {
        s.isvalid = false;
    }

    S& operator =(S&& s)
    {
        value = s.value;
        isvalid = s.isvalid;

        s.isvalid = false;
        return *this;
    }

    int value;
    bool isvalid;
};

std::ostream& operator <<(std::ostream& outp, const S& s)
{
    outp << s.value << '(' << std::boolalpha << s.isvalid << ')';
    return outp;
}

int main()
{
    std::vector<S> v = { 1,2,3,4,5 };
    const auto& a1 = v.at(0);
    const auto& a2 = v.at(2);
    const auto& a3 = v.at(4);

    std::cout << a1 << ' ' << a2 << ' ' << a3 << '\n';

    std::remove_if(v.begin(), v.end(), [](const auto& x) { return x.value == 3; });

    std::cout << a1 << ' ' << a2 << ' ' << a3 << '\n';
}

<强>输出

1(true) 3(true) 5(true)
1(true) 4(true) 5(false)

底线:您的引用仍指向之前的相同插槽,但元素已经(a)移动分配给其他内容,或(a)不再包含指定内容。在执行容器修改时使用对容器内容的引用时要小心。

我保留对std::erase来电的评论,因为我根本不知道你在做什么。据我所知,这甚至都不是标准库中的一个函数(这不是我第一次错过一个新函数,但是对于cppreference的挖掘没有产生任何结果,所以请考虑它的价值)。