stl使用中的经验(十三)--若类是一个函数子,则应使它可配接

我们先看一个例子:

#include <iostream>
#include <vector> 
#include <algorithm> 
#include <iterator>
using namespace std;

class Widget
{
public:
	Widget(int a) : m_a(a)
	{
	}
	
	int value() const
	{
		return m_a;
	}
private:
	int m_a{0};		
}; 

bool isTest(Widget* pw)
{
	return pw->value() % 2;
}


int main()
{
	typedef vector<Widget*> WdgVec;
	typedef vector<Widget*>::iterator WdgIter;
	
	WdgVec v;
	
	for(int index = 0; index < 10; ++index)
	{
		v.push_back(new Widget(index));
	}
	
	WdgIter it = find_if(v.begin(), v.end(), isTest);
	
	if(it != v.end())
	{
		cout << (*it)->value();
	}
	
//	WdgIter iter = find_if(v.begin(), v.end(), not1(isTest));
//	if(iter != v.end())
//	{
//		cout << (*iter)->value();
//	}
	return 0;
}

上面的例子中,我们能够看到,首先我们定义了一个类,然后提供一个函数判断某个Widget 类的指针所指的对象是否足够可测试。

我们如果想找到第一个满足 isTest()条件的指针对象,相对来说是比较简单的,使用算法 find_if就能找到。但是如果我们想找到第一给不满足 isTest()条件的指针对象呢?下面的这个函数能得到想要的结果吗?

WdgIter iter = find_if(v.begin(), v.end(), not1(isTest));
if(iter != v.end())
{
	cout << (*iter)->value();
}

其实上面的这个函数是不能通过编译的。报错信息如下:

[Error] 'bool(Widget*)' is not a class, struct, or union type

理解一下,也就是说,在调用 not1函数的时候,我们提供的 isTest()函数作为一个基本的函数指针,其实是缺少not1函数所需要的类型定义的。所以,我们需要在not1函数被调用之前,对基本函数指针isTest做一些类型定义的工作。

WdgIter iter = find_if(v.begin(), v.end(), not1(ptr_fun(isTest)));

上面函数,在调用not1函数之前,先对isTest()函数应用了ptr_fun,而ptr_fun的作用也是很简单的做了一些类型定义的工作。

stl中的四个标准的函数配接器(not1, not2, bind1st, bind2nd)都需要一些特殊的类型定义。非标准的、与stl兼容的配接器通常也是如此。

提供了这些必要的类型定义的函数对象称作是可配接的函数对象,可配接的函数对象能够和其他的stl的组件协同工作,因此,需要尽可能的使自己编写的函数对象是可配接的。

上面我们所说的这些特殊的类型定义是:argument_type, first_argument_type, result_type, 和 second_argument_type

而我们在编写代码的过程中,除非是自定义配接器,否则我们并不需要知道这些类型定义的具体细节。主要是因为我们可以让函数子从特定的基类(基结构)继承,来简便地提供这些类型定义。

如果函数子类的operator()函数只有一个参数,则从std::unary_function继承,如果有两个函数,则从std::binary_function继承。

但由于unary_functionbinary_functionstl 提供的模板,所以我们不能直接继承它们。相反,必须继承它们所产生的结构。也就要求我们必须指定某些类型实参。

我们在前面的章节中已经使用过上面这两种结构。

template<typename T>
class BadValue : public unary_function<int, bool>
{
public:
	BadValue(const T&){}

	bool operator()(const int&) const 
	{
		return true;
	}
private:
	const T m_t;	
};
struct ValueNameCompare : public std::binary_function<Widget, Widget, bool>
{
        bool operator()(const Widget&, const Widget& ) const 
        {
            return true;
        }
}

上面的两个例子中我们可以看到,第一个是类,第二个则是结构体,这主要是是因为,第一个BadValue中有私有成员变量,也就是所谓的状态,而类是封装状态的一种常见形式,但是ValueNameCompare 中没有任何的状态信息,因此我们可以将其声明为结构体。

上面的例子中我们可以看到,operator()函数的参数是const Widget&,但是我们传给binary_function 的却是Widget

一般情况下,我们传给binary_functionunary_function的非指针类型参数需要去掉const和引用。如果是指针类型,则两者完全相同,如下:

struct ValueNameCompare : public std::binary_function<const Widget*, const Widget*, bool>
{
    bool operator()(const Widget*, const Widget*) const
    {
        return true;
    }
}

了解了上面的信息,我么能针对刚开始的测试用力也就有了比较直接了当的写法。

#include <iostream>
#include <vector> 
#include <algorithm> 
#include <iterator>
using namespace std;

class Widget{
	public:
	Widget(int a) : m_a(a)
	{
	}
	
	int value() const
	{
		return m_a;
	}
	
private:
	int m_a;
};

template<typename T>
class isTest : public unary_function<Widget, bool> 
{
public:
	
	isTest(const T& t) : m_a(t){
	}
	
	bool operator()(const Widget& w) const
	{
		return w.value() == 2;
	}
	
private:
	T m_a;	
}; 

struct valueCompare : public binary_function<Widget, Widget, bool>
{
	bool operator()(const Widget& w, const Widget& w1) const
	{
		return w.value() == w1.value();
	}
};

int main()
{
	typedef vector<Widget> WdgVec;
	typedef vector<Widget>::iterator WdgIter;
	
	WdgVec v;
	
	Widget w1(1);
	Widget w2(2);
	Widget w3(3);
	v.push_back(w1);
	v.push_back(w2);
	v.push_back(w3);
	
	WdgIter it = find_if(v.begin(), v.end(), not1(isTest<int>(2)));
	if(it != v.end())
	{
		cout << (*it).value() << " ";
	}
	
	Widget w(3);
	
	WdgIter iter = find_if(v.begin(), v.end(), bind2nd(valueCompare(), w));
	if(iter != v.end())
	{
		cout << (*iter).value();
	}
	return 0;
}

上面的例子,我们首先定义了一个Widget类。有一个成员函数。然后写了两个个函数子,分别继承自unary_functionbinary_function,第一个判断传入的对象Widget的成员变量m_a是否等于2,第二个则比较容器中和传进的另外的 Widget对象 w的成员变量 m_a的值是否相等。

运行结果为 1 3;分别找到了对应的元素的值。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值