effective stl 第46条:考虑使用函数对象而不是函数作为STL算法的参数

void Function1()
{
    vector<double> v;
    //将函数对象传给sort()
    sort(v.begin(), v.end(), greater<double>());
}
void Function2()
{
    vector<double> v;
    //将函数传给sort()
    sort(v.begin(), v.end(), doubleGreater);
}
//经过测试,FUnction1的性能比2快的多

这个行为的解释很简单:内联。如果一个函数对象的operator()函数被声明为内联(不管显式地通过inline或者隐式地通过定义在它的类定义中),编译器就可以获得那个函数的函数体,而且大部分编译器喜欢在调用算法的模板实例化时内联那个函数。在上面的例子中,greater< double>::operator()是一个内联函数,所以编译器在实例化sort时内联展开它。结果,sort没有包含一次函数调用,而且编译器可以对这个没有调用操作的代码进行其他情况下不经常进行的优化。

使用doubleGreater的时候,实际上是将函数转化为对应的函数指针。

void sort(vector<double>::iterator first, // 区间起点
vector<double>::iterator last, // 区间终点
bool (*comp)(double, double)); // 比较函数
#include<iostream>
#include<vector>
#include<algorithm>
#include<functional>
#include<set>
#include<iterator>

using namespace std;

inline
bool doubleGreater(double d1, double d2)
{
    return d1 > d2;
}

void Function1()
{
    vector<double> v;
    //将函数对象传给sort()
    sort(v.begin(), v.end(), greater<double>());
}
void Function2()
{
    vector<double> v;
    //将函数传给sort()
    sort(v.begin(), v.end(), doubleGreater);
}
//经过测试,FUnction1的性能比2快的多
void test1()
{
    //此test在vs2013上能编译
    set<string> s;
    transform(s.begin(), s.end(), ostream_iterator<string::size_type>(cout, "\n"), mem_fun_ref(&string::size));
    //假设不能编译通过,不能编译的原因是,该平台在处理const成员函数的时候有一个错误
}
//假设test1不能通过编译,则解决方法如下:
struct StringSize :
    public unary_function<string, string::size_type>{
    string::size_type operator()(const string& s)const
    {
        return s.size();
    }
};
void test2()
{
    set<string> s;
    //dosomething
    //将函数变为函数对象,即可编译运行通过
    transform(s.begin(), s.end(), ostream_iterator<string::size_type>(cout, "\n"), StringSize());
    //输出的是每个字符串的长度
}

//函数对象由于函数本身的理由三:这样能避免一些微妙的、语言本省的缺陷
//在某些情况下,有些看似合理的代码会被编译器以一些合法但是又含糊不清的理由拒绝
//例如,当一个函数模板的实例化名称并不完全等同于一个函数的名称时,就会出现这样的情况
//下边是一个例子
template<typename FPType>
FPType average(FPType val1, FPType val2)
{
    return (val1 + val2) / 2;
}
template<typename InputIter1,typename InputIterator2>
void writeAverages(InputIter1 begin1, InputIter1 end1, InputIterator2 begin2, ostream& s)
{
    transform(begin1, end1, begin2, ostream_iterator<typename iterator_traits<InputIter1>::value_type>(s, "\n"),
        average<typename iterator_traits<InputIter1>::value_type>);
}
//许多的编译器会接受这个代码,但是C++标准是拒绝的,因为可能还存在另一个average的函数模板,它只带一个类型的参数。
//如果这样,average<typename iterator_traits<InputIter1>::value_type>,就会有二义性,解决方法很简单,只需用
//一个函数对象来代替函数即可:
template<typename FPType>
struct Average :
    public binary_function<FPType, FPType, FPType>{
    FPType operator()(FPType val1, FPType val2) const
    {
        return average(val1, val2);
    }
};
template<typename InputIter1, typename InputIterator2>
void WriteAverages(InputIter1 begin1, InputIter1 end1, InputIterator2 begin2, ostream& s)
{
    transform(begin1, end1, begin2, ostream_iterator<typename iterator_traits<InputIter1>::value_type>(s, "\n"),
        Average<typename iterator_traits<InputIter1>::value_type>());
}
int main()
{

    test1();

    return 0;
}

把函数对象作为算法的参数所带来的不仅是巨大的效率提升。在让你的代码可以编译方面,它们也更稳健。当然,真函数很有用,但是当涉及有效的STL编程时,函数对象经常更有用。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值