【问题标题】:Vector erase giving unexpected result向量擦除给出了意想不到的结果
【发布时间】:2013-07-28 13:49:39
【问题描述】:

使用 lambda 函数和函数对象从向量中擦除给出不同的结果。

我正在尝试从字符串向量中删除第三个元素。 函数对象的第 3 和第 6 个元素被删除,但 lambda 版本的代码给出了预期的结果。

我尝试了以下代码:

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

using namespace std;

int main()
{

vector<string> s;

copy(istream_iterator<string>(cin),
    istream_iterator<string>(),
    back_inserter(s));

    cout<<"S contains :"<<endl;
    for(auto x:s)
        cout<<x<<" ";
    cout<<endl;

#ifndef USE_LAMBDA    

struct Word_No{
int word_ith;
int word_count;

Word_No(int x)  :word_ith(x),word_count(0){}

bool operator () (string){
return ++word_count == word_ith;
}
};
//3rd Element remove
    s.erase(remove_if(s.begin(),s.end(),Word_No(3)),s.end());
#else
    int count =0;
    s.erase(remove_if(s.begin(),
                      s.end(),
                      [&count](string){
                         return ++count ==3; //3rd Element Remove
                            }),
                      s.end());
#endif

    cout<<"Now S contains :"<<endl;
    for(auto x:s)
        cout<<x<<" ";
}

结果:

g++ -o test test.cpp -std=gnu++0x

输入:King Queen Jack Ace Rook 骑士典当主教

输出:

S 包含:King Queen Jack Ace Rook Knight Pawn Bishop

现在 S 包含:King Queen Ace Rook Pawn Bishop //错误的结果 3 和第 6 个元素已删除。

g++ -o test test.cpp -std=gnu++0x -DUSE_LAMBDA

输入:King Queen Jack Ace Rook Knight Pawn Bishop

S 包含:King Queen Jack Ace Rook Knight Pawn Bishop

现在 S 包含:King Queen Ace Rook Knight Pawn Bishop // 正确结果

谁能解释一下这两种行为?

【问题讨论】:

    标签: algorithm c++11 stl


    【解决方案1】:

    您是remove_if 实现的受害者,Josuttis 的 C++ 标准库对此进行了详细描述。

    总结是:

    第 3 和第 6 个元素在没有 lambda 版本的情况下被删除,因为 remove_if 在处理期间会在内部复制谓词。

    它在内部使用find_if 来查找应该删除的元素。 稍后,算法使用谓词的副本来处理剩余的元素,如果有的话,使用remove_if_copy

    在 lambda 的情况下,当您通过引用传递参数时,内部使用的 lambda 对象 remove_if 共享相同状态

    这是remove_if的大概实现

    template<typename FwdItr, typename Pred>
    FwdItr std::remove_if(FwdItr b, FwdItr e, Pred f)
    {
        s = find_if(b,e,f);
        if(s==e)
            return b;
        else
         {
            FwdItr temp = b;
            return remove_copy_if(++temp, e, b, f);
         }
    }
    

    【讨论】:

    • +1。作为一个符合标准的扩展,libc++ (libcxx.llvm.org) 无论有没有-DUSE_LAMBDA,都能得到正确的答案。这(没有内部仿函数副本)对于 libc++ 中的所有其他算法也是如此。使用这个符合标准的扩展来实现它并不需要更多的努力。
    • @P0W 是否可以使用 lambda 函数重新创建相同的场景?
    • @user1446258 是使 lambda 函数 mutable
    【解决方案2】:

    标准定义并没有说这个函数会以特定的顺序使用谓词,它说的是:

    1 要求:*first 的类型应满足 MoveAssignable 要求(表 22)。

    2 效果:消除迭代器 i 引用的所有元素 以下对应的范围 [first,last) 条件成立:*i == value, pred(*i) != false。

    3 返回:结果范围的结尾。

    4 备注:稳定 (17.6.5.7)。

    5 复杂性:恰好最后 - 相应的第一个应用程序 谓词。

    6 注意:[ret,last) 范围内的每个元素,其中 ret 是 返回值,具有有效但未指定的状态,因为 算法可以通过移动元素来消除元素 原来在那个范围内。

    因此,您不应假设谓词的使用顺序与向量元素的使用顺序相同。

    最后,你可以简单地写

    ..
    s.erase(s.begin()+2);
    ..
    

    删除向量的第三个元素。

    最好的问候

    【讨论】:

      猜你喜欢
      • 2016-12-17
      • 2019-05-19
      • 2017-01-05
      • 2021-10-14
      • 1970-01-01
      • 1970-01-01
      • 2021-12-17
      • 2021-04-22
      • 2016-02-13
      相关资源
      最近更新 更多