仿函数的一点问题

转角天边 2013-02-28 03:20:01
请人解释一下原因,先上代码

#include "stdafx.h"
#include <iostream>
#include <list>
#include <algorithm>
using namespace std;

class Nth
{
private:
int nth;
int count;
public:
Nth(int n):nth(n),count(0){}
bool operator()(int)
{
return ++count==nth;
}
};
int main(int argc, char* argv[])
{
list<int> coll;
list<int>::iterator pos;
for(int i=1;i<=9;++i)
{
coll.push_back(i);
}

pos=remove_if(coll.begin(),coll.end(),Nth(3));
//删除逻辑终点与实际终点间的元素
coll.erase(pos,coll.end());

for(pos=coll.begin();pos!=coll.end();++pos)
{
cout<<*pos<<" ";
}

cout<<endl;
return 0;
}

程序输出的结果为:
1 2 4 5 7 8 9
为什么6也会被删除,书上的解释是remove_if会于算法内部保留判断式的一份副本,当find_if()找到应被移除的第一个元素后,接下来它使用传进来的判断式的副本去处理剩余元素。对这段话不太理解,求指教
...全文
527 35 打赏 收藏 转发到动态 举报
写回复
用AI写文章
35 条回复
切换为时间正序
请发表友善的回复…
发表回复
rocktyt 2013-02-28
  • 打赏
  • 举报
回复
引用 23 楼 classpatterns 的回复:
刚看了下 VS里的代码 C/C++ code?1234567891011template<class _FwdIt, class _Pr> inline _FwdIt remove_if(_FwdIt _First, _FwdIt _Last, _Pr _Pred) { // remove each satisfying _Pred _Fi……
gnu的版本见14楼 你下面的那个只是示例版本,只是为了方便理解而写的,并没有哪套stl在用那样的代码
weiym 2013-02-28
  • 打赏
  • 举报
回复
参考 effective STL item 39: Make predicates pure functions
weiym 2013-02-28
  • 打赏
  • 举报
回复
用法不对, 仿函数要求相同的值传进去会返回相同的结果, 你这里根据仿函数的调用次数,相同的值会返回不同的结果
意吟 2013-02-28
  • 打赏
  • 举报
回复
#31楼有点问题,

    #include <stdio.h>
    #include <iostream>
    #include <list>
    #include <algorithm>
    using namespace std;


    list<int> coll;
    list<int>::iterator pos;
    int iCount = 1;

    bool bCout = true;

    class Nth
    {
    private:
        int  nth;
        int count;
    public:
        Nth(int n):nth(n),count(0){}
        bool operator()(int)
        {
            bool bret = ++count == nth;

            cout << "No." << iCount++ << ":";
            for(pos=coll.begin();pos!=coll.end();++pos)
            {
                cout<<*pos<<" ";
            }
            cout <<endl;
            return  bret;
        }
    };
    int main(int argc, char* argv[])
    {
        for(int i=1;i<=9;++i)
        {
            coll.push_back(i);
        }

        pos=remove_if(coll.begin(),coll.end(),Nth(3));
        //删除逻辑终点与实际终点间的元素
        //coll.erase(pos,coll.end());

        for(pos=coll.begin();pos!=coll.end();++pos)
        {
            cout<<*pos<<" ";
        }

        cout<<endl;
        return 0;
    }
我把每一次的结果都打印出来,

No.1:1 2 3 4 5 6 7 8 9
No.2:1 2 3 4 5 6 7 8 9
No.3:1 2 3 4 5 6 7 8 9
No.4:1 2 3 4 5 6 7 8 9
No.5:1 2 4 4 5 6 7 8 9
No.6:1 2 4 5 5 6 7 8 9
No.7:1 2 4 5 5 6 7 8 9
No.8:1 2 4 5 7 6 7 8 9
No.9:1 2 4 5 7 8 7 8 9
1 2 4 5 7 8 9 8 9
从上面分析, 到No.3 的时候 list 未发生变化, 直到 No.4 ~ No.6 一直在发生变化
意吟 2013-02-28
  • 打赏
  • 举报
回复

#include <stdio.h>
#include <iostream>
#include <list>
#include <algorithm>
using namespace std;


list<int> coll;
list<int>::iterator pos;

class Nth
{
private:
	int  nth;
    int count;
public:
	Nth(int n):nth(n),count(0){}
	bool operator()(int)
	{
        bool bret = ++count == nth;
        if (bret)
        {
            
            for(pos=coll.begin();pos!=coll.end();++pos)
            {
                cout<<*pos<<" ";
            }
            cout <<endl;
        }
        return  bret;
	}
};
int main(int argc, char* argv[])
{
	for(int i=1;i<=9;++i)
	{
		coll.push_back(i);
	}

	pos=remove_if(coll.begin(),coll.end(),Nth(3));
	//删除逻辑终点与实际终点间的元素
	coll.erase(pos,coll.end());

	for(pos=coll.begin();pos!=coll.end();++pos)
	{
		cout<<*pos<<" ";
	}

	cout<<endl;
	return 0;
}
我把 list<int> coll; list<int>::iterator pos; 升级为全局, 当找到目标,方便输出 list的 情况 如下:

1 2 3 4 5 6 7 8 9
1 2 4 5 5 6 7 8 9
1 2 4 5 7 8 9
意吟 2013-02-28
  • 打赏
  • 举报
回复
第一次找到 remove_if : vector 的结果:1 2 4 5 6 7 8 9 9 第二次找到 remove_if : vector 的结果:1 2 4 5 7 8 9 8 9 (注意:iter 已经走到4这个位置)
FingerStyle 2013-02-28
  • 打赏
  • 举报
回复
引用 26 楼 anhuizhuanjiao 的回复:
我想我弄懂了, remove_if()函数通过调用find_if(),找到第一个满足条件的元素,也就是3,将3删除,然后调用remove_copy_if()函数处理剩余的元素,而remove_copy_if()函数使用的原始状态下的Nth,因为find_if()修改的只是判断式的一个副本,原始状态的Nth并未变化,导致剩余元素的第三个元素又被remove_copy_if……
是的。应该是MS的STL版本太烂吧。 template <class ForwardIterator, class UnaryPredicate> ForwardIterator remove_if (ForwardIterator first, ForwardIterator last, UnaryPredicate pred) { ForwardIterator result = first; while (first!=last) { if (!pred(*first)) { *result = std::move(*first); ++result; } ++first; } return result; } 这个版本看起来 完全没问题啊。
FingerStyle 2013-02-28
  • 打赏
  • 举报
回复
引用 25 楼 wy_xylyan 的回复:
C/C++ code?12345678910111213141516171819202122232425262728293031323334353637383940#include <stdio.h>#include <iostream>#include <list>#include <algorithm>using namespace std; class Nth{pr……
不是销毁重新计数, 而是remove_if 到 find_if 的过程中有拷贝一个新的仿函数。在find_if里的++count对remove_if都是无效的。 所以把 count 改为static类型 也是可行的。
意吟 2013-02-28
  • 打赏
  • 举报
回复
因此, 把count改为static ,那么3 就能删除了

#include <stdio.h>
#include <iostream>
#include <list>
#include <algorithm>
using namespace std;

class Nth
{
private:
	int  nth;
public:
	Nth(int n):nth(n){}
	bool operator()(int)
	{
        static int count = 0;
        cout << count << "," << nth << endl;
        return  ++count==nth; 
	}
};
int main(int argc, char* argv[])
{
	list<int> coll;
	list<int>::iterator pos;
	for(int i=1;i<=9;++i)
	{
		coll.push_back(i);
	}

	pos=remove_if(coll.begin(),coll.end(),Nth(3));
	//删除逻辑终点与实际终点间的元素
	coll.erase(pos,coll.end());

	for(pos=coll.begin();pos!=coll.end();++pos)
	{
		cout<<*pos<<" ";
	}

	cout<<endl;
	return 0;
}

0,3
1,3
2,3
3,3
4,3
5,3
6,3
7,3
8,3
1 2 4 5 6 7 8 9
转角天边 2013-02-28
  • 打赏
  • 举报
回复
我想我弄懂了, remove_if()函数通过调用find_if(),找到第一个满足条件的元素,也就是3,将3删除,然后调用remove_copy_if()函数处理剩余的元素,而remove_copy_if()函数使用的原始状态下的Nth,因为find_if()修改的只是判断式的一个副本,原始状态的Nth并未变化,导致剩余元素的第三个元素又被remove_copy_if()函数删除了,所以输出的是1 2 4 5 7 8 9
意吟 2013-02-28
  • 打赏
  • 举报
回复

#include <stdio.h>
#include <iostream>
#include <list>
#include <algorithm>
using namespace std;

class Nth
{
private:
	int  nth;
	int  count;
public:
	Nth(int n):nth(n),count(0){}
	bool operator()(int)
	{
        cout << count << "," << nth << endl;
        return  ++count==nth; 
	}
};
int main(int argc, char* argv[])
{
	list<int> coll;
	list<int>::iterator pos;
	for(int i=1;i<=9;++i)
	{
		coll.push_back(i);
	}

	pos=remove_if(coll.begin(),coll.end(),Nth(3));
	//删除逻辑终点与实际终点间的元素
	coll.erase(pos,coll.end());

	for(pos=coll.begin();pos!=coll.end();++pos)
	{
		cout<<*pos<<" ";
	}

	cout<<endl;
	return 0;
}
输出结果:

0,3
1,3
2,3
0,3
1,3
2,3
3,3
4,3
5,3
1 2 4 5 7 8 9
从此可见, 当remove_if找到对象时, Nth 就会被销毁,重新计数。
  • 打赏
  • 举报
回复
remove_if() 本身不会删除数据,而是把数据一到容器的最后去了。 erase()才是删除数据。 pos=remove_if(coll.begin(),coll.end(),Nth(3)); 这个时候,容器的内容是 1 2 4 5 6 7 8 9 3 执行完这一句,pos指向了 4, coll.erase(pos,coll.end()); 这一句是,从pos这个位置开始找,找到第3个数据(即是6),删除 因此就是 1 2 4 5 7 8 9 (3)3 是coll.end()位置,没有输出
FingerStyle 2013-02-28
  • 打赏
  • 举报
回复
刚看了下 VS里的代码
template<class _FwdIt,
	class _Pr> inline
	_FwdIt remove_if(_FwdIt _First, _FwdIt _Last, _Pr _Pred)
	{	// remove each satisfying _Pred
	_First = _STD find_if(_First, _Last, _Pred);
	if (_First == _Last)
		return (_First);	// empty sequence, all done
	else
		return (_Rechecked(_First,
			_Remove_if(_Unchecked(_First), _Unchecked(_Last), _Pred)));
	}
里面套了一层find_if 但是_Pred不是引用过去的, find_if里count++之后 跳出remove_if里的pred的count还是0 所以隔3个 还会find一次。。 这个算MS写的代码问题吧。 给你看下另一个版本的remove_if 这个应该不会出现问题。
template <class ForwardIterator, class UnaryPredicate>
  ForwardIterator remove_if (ForwardIterator first, ForwardIterator last,
                             UnaryPredicate pred)
{
  ForwardIterator result = first;
  while (first!=last) {
    if (!pred(*first)) {
      *result = *first;
      ++result;
    }
    ++first;
  }
  return result;
}
mujiok2003 2013-02-28
  • 打赏
  • 举报
回复
STL中,谓词/Pred一般是无状态的,就是普通函数的泛化。如果,是有状态的,需要把状态用引用/指针来表示,否则就会出现这样的问题。
FingerStyle 2013-02-28
  • 打赏
  • 举报
回复
试了试 第二种 还是不对。。
FingerStyle 2013-02-28
  • 打赏
  • 举报
回复
bool operator()(int n) { return (n == 3); } 这样可以 或者 class Nth { private: int nth; int count; public: Nth() { } bool operator()(int) { return (++count==nth); } void SetNC(int n, int c) { nth = n; count = c; } }; Nth nth; nth.SetNC(3, 0); pos=remove_if(coll.begin(),coll.end(), nth);
mujiok2003 2013-02-28
  • 打赏
  • 举报
回复
引用 15 楼 anhuizhuanjiao 的回复:
引用 12 楼 mujiok2003 的回复:看看源码,很清楚。Pred都是拷贝。 C/C++ code?12345678910111213141516171819202122template<class _FwdIt, class _Pr> inline _FwdIt _Remove_if(_FwdIt _First, _FwdIt _Last, _P……
通过find_if找到了3, 再从4开始,重新计数:4覆盖到3的位置,5覆盖到4的位置,跳过6, 7覆盖到5的位置....
ri_aje 2013-02-28
  • 打赏
  • 举报
回复
呵,刚看明白楼主要干吗,原来是删除第三个元素,之前理解成没三个删除一个了。如果是前者,有必要这么麻烦吗,写个循环,计数器为三的时候,删除当前节点,跳出循环不就完了吗。
mujiok2003 2013-02-28
  • 打赏
  • 举报
回复
使用引用语义


class Nth
{
private:
	int  nth;
	std::shared_ptr<int>  count;
public:
	Nth(int n):nth(n),count( new int(0)){}
	bool operator()(int)
	{
		return  ++*count==nth;
	}
};
int main(int argc, char* argv[])
{
	list<int> coll;
	list<int>::iterator pos;
	for(int i=1;i<=9;++i)
	{
		coll.push_back(i);
	}

	pos=remove_if(coll.begin(),coll.end(),Nth(3));
	//删除逻辑终点与实际终点间的元素
	coll.erase(pos,coll.end());

	for(pos=coll.begin();pos!=coll.end();++pos)
	{
		cout<<*pos<<" ";
	}

	cout<<endl;
	return 0;
}
//1,2,4,5,6,7,8,9
rocktyt 2013-02-28
  • 打赏
  • 举报
回复
引用 11 楼 anhuizhuanjiao 的回复:
引用 9 楼 rocktyt2 的回复:引用 3 楼 anhuizhuanjiao 的回复:引用 2 楼 rocktyt2 的回复:看了vs的和gnu的2个实现,都是先调用find_if找到第一个位置并删除,然后再继续往下挨个处理下去 因为find_if调用时也是值传递,不会对原本的判断式造成影响,所以会出现这样的结果 按你的例子来解释的话,实际的操作是这样的 先用……
我从来没有说过循环往下找 过程中一共有出现2个判断式,find_if里的和remove_if里的,find_if只在一开始执行一次,然后就一直是remove_if里的那个了,很难理解吗
加载更多回复(15)

64,640

社区成员

发帖
与我相关
我的任务
社区描述
C++ 语言相关问题讨论,技术干货分享,前沿动态等
c++ 技术论坛(原bbs)
社区管理员
  • C++ 语言社区
  • encoderlee
  • paschen
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
  1. 请不要发布与C++技术无关的贴子
  2. 请不要发布与技术无关的招聘、广告的帖子
  3. 请尽可能的描述清楚你的问题,如果涉及到代码请尽可能的格式化一下

试试用AI创作助手写篇文章吧