8 functors/functor objects
8.1 conception of functor
functor,是定義了operator()的對象。
eg:
FunctionObjectType fo;
// ...
fo(...)
其中fo()調(diào)用仿函數(shù)fo的operator()而非函數(shù)fo()。
可將functor視為一般函數(shù),但更復(fù)雜,eg:
class FunctonObjectType
{
public:
void operator() (){ // statements }
};
functor定義形式復(fù)雜,但優(yōu)點:
1. functor較一般函數(shù)更靈巧。因為functor可有兩個狀態(tài)不同的實體。
2.functor有其類型,故可將其類型作為template參數(shù)。
3. functor執(zhí)行速度更快。
8.1.1 functor作sort criteria
/* 需將某些class objects以已序形式置于容器中。
* 但或不想或不能,總之無法使用一般的operator<來排序。
* 要以某種特定規(guī)則(?;谀承┏蓡T函數(shù))來排序,此時可用functor。
*/
// fo/sort1.cpp
#include <iostream>
#include <string>
#include <set>
#include <algorithm>
using namespace std;
class Person
{
public:
string firstname() const;
string lastname() const;
// ...
};
/* class fot function predicate
* -operator() returns whether a person is less than another person
*/
class PersonSortCriterion
{
public:
bool operator() (const Person& p1, const Person& p2) const
{
/* a person is less than another person
* - if the last name is less
* - if the last name is equal and the first name is less
*/
return p1.lastname() < p2.lastname() ||
(!(p2.lastname() < p1.lastname()) &&
p1.firstname() < p2.firstname() );
}
};
int main()
{
// declare set type with special sorting criterion
typedef set<Person, PersonSortCriterion> PersonSet;
// create such a collection
PersonSet col1;
//...
// do something with the elements
PersonSet::iterator pos;
for(pos = col1.begin(); pos != col1.end(); ++pos)
{
//...
}
// ...
}
前面有相關(guān)代碼,可參考p199。
8.1.2 擁有internal state的functor
// 展示functor 模擬函數(shù)在同一時刻下?lián)碛卸鄠€狀態(tài)
// fo/genera1.cpp
#include <iostream>
#include <list>
#include <algorithm>
#include "../stl/print.hpp"
using namespace std;
class IntSequence
{
private:
int value;
public:
// constructor
IntSequence (int initialValue) : value(initialValue){}
// "function call"
int operator() ()
{
return value++;
}
};
int main()
{
list<int> col1;
// insert value from 1 to 9
generate_n (back_inserter(col1), // start
9, // number of elements
IntSequence(1) ); // generate values
PRINT_ELEMENTS(col1);
// replace second to last element but one with values
// starting at 42
generate (++col1.begin(), // start
--col1.end(), // end
IntSequence(42) ); // generate values
PRINT_ELEMENTS(col1);
}
output:
functor是passed by value,而不是passed by reference: passed by value優(yōu)點是可傳遞常量或臨時表達(dá)式,缺點不能改變隨參數(shù)而來最終的functor state。
有兩種辦法從“運行了functor”的算法中獲得結(jié)果:
- by reference方式傳遞functor。
- 使用for_each()。(見下一節(jié))
// fo/genera2.cpp
#include <iostream>
#include <list>
#include <algorithm>
#include "../stl/print.hpp"
using namespace std;
class IntSequence
{
private:
int value;
public:
// constructor
IntSequence(int initialValue) : value(initialValue){}
// "function call"
int operator() ()
{
return value++;
}
};
int main()
{
list<int> col1;
IntSequence seq(1); // integral sequence starting with 1
// insert value from 1 to 4
// - pass function object by reference
// so that it will continue with 5
generate_n<back_insert_iterator<list<int> >,
int, IntSequence& >(back_inserter(col1) , // start
4, // number of elements
seq); // generate values
PRINT_ELEMENTS(col1);
// insert values from 42 to 45
generate_n(back_inserter(col1), // start
4, // number of elements
IntSequence(42)); // generates values
PRINT_ELEMENTS(col1);
// continue with first sequence
// - pass function object by value
// so that it will continue with 5 again
generate_n(back_inserter(col1), 4,seq);
PRINT_ELEMENTS(col1);
// continue with first sequence again
generate_n(back_inserter(col1), 4, seq);
PRINT_ELEMENTS(col1);
}
output:
8.1.3 for_each()返回值
for_each()可以返回其functor,其它算法都無該特性。
// 使用for_each()返回值,處理一個序列的平均值
// fo/foreach3.cpp
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
// function object to process the mean value
class MeanValue
{
private:
long num; // num of elements
long sum; // sum of all element values
public:
// constructor
MeanValue() : num(0), sum(0){}
// "function call"
// - process one more elements of the sequence
void operator() (int elem)
{
num++; // increment
sum += elem; // add value
}
// return mean value
double value()
{
return static_cast<double>(sum)/static_cast<double>(num);
}
};
int main()
{
vector<int> col1;
// insert elements from 1 to 8
for(int i = 1; i <= 8; ++i)
{
col1.push_back(i);
}
// process and print mean value
MeanValue mv = for_each(col1.begin(), col1.end(), MeanValue());
cout << "mean value: " << mv.value() << endl;
}
程序說明:返回的functor被賦值給mv,故可調(diào)用mv.value()查詢其狀態(tài)。
output:
8.1.4 predicates && functors
predicate,即返回bool值的一個函數(shù)或functor,但對STL而言并非所有返回bool的函數(shù)都是合法的predicate。
// fo/removeif.cpp
#include <iostream>
#include <list>
#include <algorithm>
#include "../stl/print.hpp"
using namespace std;
class Nth
{ // function object that returns true for the nth call
private:
int nth; // call for which to return true
int count;
public:
Nth(int n) : nth(n), count(0){}
bool operator() (int)
{
return ++count == nth;
}
};
int main()
{
list<int> col1;
// insert elements from 1 to 9
for (int i = 1; i <= 9; ++i)
{
col1.push_back(i);
}
PRINT_ELEMENTS(col1, "col1: ");
// remove third element
list<int>::iterator pos;
pos = remove_if(col1.begin(), col1.end(), Nth(3));
col1.erase(pos, col1.end());
PRINT_ELEMENTS(col1, "nth removed: ");
}
output:
結(jié)果及說明:
結(jié)果刪除了第3和第6個元素,
原因在于remove_if()標(biāo)準(zhǔn)實現(xiàn)內(nèi)部保留predicate的一份副本:
template <class ForwIter, class Predicate>
ForwIter std::remove_if(ForwIter beg, ForwIter end, Predicate op)
{
beg = find_if(beg, end, op);
if (beg == end)
{
return beg;
}
else
{
ForwIter next = beg;
return remove_copy_if(++next, end, beg, op);
}
}
remove_if()使用find_if()來查找應(yīng)被移除的第一個元素,
然后使用predicate op的副本處里剩余的元素,
故原始狀態(tài)的Nth再次被調(diào)用,即會移除剩余元素的第三個元素。
由上,predicate不應(yīng)該因為被調(diào)用而改變自身狀態(tài)。
當(dāng)然,避免上述問題可改變下remove_if()的標(biāo)準(zhǔn)實現(xiàn):
beg = find_if(beg, end, op); 語句替換為
while(beg != end && !op(*beg)){ ++beg;}
8.2 預(yù)定義的functor
使用這些functors,須#include <functional>

bind1st(op, para)指將para綁定到op的第一個參數(shù),bind2nd(op,para)指將para綁定到op的第二個參數(shù)。(可見p311的fopow1.cpp)
8.2.1 function adapter(函數(shù)配接器)
function adapter指能 將functor和另一個functor(或某個值或一般函數(shù))結(jié)合起來的functor。function adapter也聲明于<functional>。

functional compositon(功能復(fù)合、函數(shù)復(fù)合):把多個functor結(jié)合起來形成強大的表達(dá)式。
8.2.2 對成員函數(shù)設(shè)計的函數(shù)配接器
C++標(biāo)準(zhǔn)庫提供了一些額外的函數(shù)配接器,通過它們,可對群集內(nèi)每個元素調(diào)用其成員函數(shù)。

eg:
// 利用mem_fun_ref,對vector內(nèi)每個對象調(diào)用其成員函數(shù)
// fo/memfun1a.cpp
class Person
{
private:
std::string name;
public:
// ...
void print() const
{
std::cout << name << std::endl;
}
void printWithPrefix (std::string prefix) const
{
std::cout << prefix << name << std::endl;
}
};
void foo(const std::vector<Person>& col1)
{
using std::for_each;
using std::bind2nd;
using std::mem_fun_ref;
// call member function print() for each element
for_each(col1.begin(), col1.end(),
mem_fun_ref(&Person::print));
// call member function printWithPrefix() for each element
// - "person: " is passed as an argument to the member function
for_each(col1.begin(), col1.end(),
bind2nd(mem_fun_ref(&Person::printWithPrefix), "person: ") );
}
不能直接把一個成員函數(shù)傳給算法,for_each()會針對第三參數(shù)傳來的指針調(diào)用operator() 而非調(diào)用該指針?biāo)傅暮瘮?shù)(可見p126的for_each()實現(xiàn))。用配接器會將operator()調(diào)用操作做適當(dāng)轉(zhuǎn)換。
eg:
for_each(col1.begin, col1.end(), &Person::print);
// error:can't call operator() for a member function potiner
8.2.3 對一般函數(shù)設(shè)計的函數(shù)配接器

bool check(int elem);
pos = find_if(col1.begin(), col1.end(),
not1(ptr_fun(check) ) ); // 查找第一個令檢驗失敗的元素
pos = find_if (col1.begin(), col1.end(),
bind2nd(ptr_fun(strcmp), "") ) ;
// 采用strcmp()將每個元素與空的C-string比較。
8.2.4 user-defined functor使用function adapter
若希望user-defined functor能和function adapter搭配使用,須滿足一些條件:提供類型成員反映其參數(shù)和返回值類型。
C++標(biāo)準(zhǔn)庫提供了一些結(jié)構(gòu):
template <class Arg, class Result>
struct unary_function
{
typedef Arg argument_type;
typedef Result result_type;
};
template <class Arg1, class Arg2, class Result>
struct binary_function
{
typedef Arg1 first_argument_type;
typedef Arg2 second_argument_type;
typedef Result result_type;
};
如此只要繼承上述某種形式,即可滿足adaptable條件。
eg:
// fo/fopow.hpp
#include <funcitonal>
#include <cmath>
template <class T1, class T2>
struct fopow : public std::binary_function<T1, T2, T1>
{
T1 operator() (T1 base, T2 exp) const
{
return std::pow(base, exp);
}
};
// fo/fopow1.cpp
#include <iostream>
#include <vector>
#include <algorithm>
#include <iterator>
using namespace std;
#include "fopow.hpp"
int main()
{
vector<int> col1;
for (int i = 1; i <= 9; ++i)
{
col1.push_back(i);
}
// print 3 raised to the power of all elements
transform(col1.begin(), col1.end(),
ostream_iterator<int>(cout, " "),
bind1st(fopow<float,int>(), 3));
cout << endl;
// print all elements raised to the power of 3
transform(col1.begin(), col1.end(),
ostream_iterator<int>(cout, " "),
bind2nd(fopow<float,int>(), 3));
cout << endl;
}
output:
8.3 組合型functor
一般而言,所有函數(shù)行為都可由functor組合實現(xiàn)。但C++標(biāo)準(zhǔn)庫未提供足夠的配接器來實現(xiàn)。
理論上,下面的compose adapter(組合型配接器)很有用。
f( g(elem) )
一元判別式g()結(jié)果作為一元判別式f()參數(shù)。
f( g(elem1, elem2) )
二元判別式g()結(jié)果作為一元判別式f()參數(shù)。
f( g(elem1), h(elem2) )
一元判別式g()和h()的結(jié)果作為二元判別式f()的參數(shù)。
但上述compose adapter未被納入標(biāo)準(zhǔn)。書中作者自定義名稱:

8.3.1 unary compose function object adapter
-
以compose_f_gx進行nested(嵌套)計算
compose_f_gx在SGI STL實現(xiàn)版本稱為compose1,compose_f_gx的可實現(xiàn)如下:
// fo/compose11.hpp
#include <functional>
/* class for the compose_f_gx adapter */
template <class OP1, class OP2>
class compose_f_gx_t :
public std::unary_function<typename OP2::argument_type, typename OP1::result_type>
{
private:
OP1 op1; // process: op1(op2(X))
OP2 op2;
public:
// constructor
compose_f_gx_t(const OP1& o1, const OP2& o2) : op1(o1), op2(o2){}
// function call
typename OP1::result_type
operator() (const typename OP2::argument_type& x) const
{
return op1(op2(X));
}
};
/* conveniences functions for the compose_f_gx adapter */
template <class OP1, class OP2>
inline compose_f_gx_t<OP1, OP2>
compose_f_gx (const OP1& o1, const OP2& o2)
{
return compose_f_gx_t<OP1, OP2>(o1, o2);
}
// 對一個序列的每個元素先 加10 再乘5
// fo/compose1.cpp
#include <iostream>
#include <vector>
#include <algorithm>
#include <functional>
#include <iterator>
#include "../stl/print.hpp"
#include "compose11.hpp"
using namespace std;
int main()
{
vector<int> col1;
// insert elements from 1 to 9
for (int i = 1; i <= 9; ++i)
{
col1.push_back(i);
}
PRINT_ELEMENTS(col1);
// for each element add 10 and multiply by 5
transform (col1.begin(), col1.end(),
ostream_iterator<int>(cout, " "),
compose_f_gx(bind2nd(multiplies<int>(), 5),
bind2nd(plus<int>(), 10) ) );
cout << endl;
}
output:
-
以compose_f_gx_hx組合兩個準(zhǔn)則
compose_f_gx_hx在SGI STL實現(xiàn)版本稱為compose2,compose_f_gx_hx可實現(xiàn)如下:
// fo/compose21.hpp
#include <functional>
/* class for the compose_f_gx_hx adapter */
template <class OP1, class OP2, class OP3>
class compose_f_gx_hx_t :
public std::unary_function<typename OP2::argument_type, typename OP1::result_type>
{
private:
OP1 op1; // process: op1(op2(x), op3(x))
OP2 op2;
OP3 op3;
public:
// constructor
compose_f_gx_gx_t (const OP1& o1, const OP2& o2, const OP3& o3) :
op1(o1), op2(o2), op3(o3){}
// function call
typename OP1::result_type
operator() (const typename OP2::argument& x) const
{
return op1(op2(x), op3(x));
}
};
/* convenience functions for the compose_f_gx_hx adapter */
template <class OP1, class OP2, class OP3>
inline compose_f_gx_hx_t<OP1, OP2, OP3>
compose_f_gx_hx (const OP1& o1, const OP2& o2, const OP3& o3)
{
return compose_f_gx_hx_t<OP1, OP2, OP3>(o1, o2, o3);
}
// 刪除序列中大于4且小于7 的元素
// fo/compose2.cpp
#include <iostream>
#include <vector>
#include <algorithm>
#include <functional>
#include "../stl/print.hpp"
#include "compose21.hpp"
using namespace std;
int main()
{
vector<int> col1;
// insert elements from 1 to 9
for (int i = 1; i <= 9; ++i)
{
col1.push_back(i);
}
PRINT_ELEMENTS(col1);
// remove all elements that are greater than four and less than seven
// - retain new end
vector<int>::iterator pos;
pos = remove_if(col1.begin(), col1.end(),
compose_f_gx_hx(logical_and<bool>(),
bind2nd(greater<int>(), 4),
bind2nd(less<int>(),7) ) );
// remove "removed" elements in col1
col1.erase(pos, col1.end());
PRINT_ELEMENTS(col1);
}
output:
8.3.2 binary compose function object adapters
二元組合函數(shù)配接器,可將兩個一元運算(分別接受不同參數(shù))的結(jié)果加以處理,稱之為compose_f_gx_hy,可能的實現(xiàn)如下:
// fo/compose22.hpp
#include <functional>
/* class for the compose_f_gx_hy adapter */
template <class OP1, class OP2, class OP3>
class compose_f_gx_hy_t :
public std::binary_function<typename OP2::argument_type,
typename OP3::argument_type,
typename OP1::result_type>
{
private:
OP1 op1; // process: op1(op2(x), op3(y))
OP2 op2;
OP3 op3;
public:
// constructor
compose_f_gx_hy_t(const OP1& o1, const OP2& o2, const OP3& o3) :
op1(o1), op2(o2), op3(o3){}
// function call
typename OP1::result_type
operator() (const typename OP2::argument_type& x,
const typename OP3::argument_type& y) const
{
return op1(op2(x), op3(y));
}
};
/* convenience function for the compose_f_gx_hy adapter */
template <class OP1, class OP2, class OP3>
inline compose_f_gx_hy_t<OP1, OP2, OP3>
compose_f_gx_hy(const OP1& o1, const OP2& o2, const OP3& o3)
{
return compose_f_gx_hy_t<OP1,OP2, OP3>(o1, o2, o3);
}
// fo/compose22.hpp
#include <functional>
/* class for the compose_f_gx_hy adapter */
template <class OP1, class OP2, class OP3>
class compose_f_gx_hy_t :
public std::binary_function<typename OP2::argument_type,
typename OP3::argument_type,
typename OP1::result_type>
{
private:
OP1 op1; // process: op1(op2(x), op3(y))
OP2 op2;
OP3 op3;
public:
// constructor
compose_f_gx_hy_t(const OP1& o1, const OP2& o2, const OP3& o3) :
op1(o1), op2(o2), op3(o3){}
// function call
typename OP1::result_type
operator() (const typename OP2::argument_type& x,
const typename OP3::argument_type& y) const
{
return op1(op2(x), op3(y));
}
};
/* convenience function for the compose_f_gx_hy adapter */
template <class OP1, class OP2, class OP3>
inline compose_f_gx_hy_t<OP1, OP2, OP3>
compose_f_gx_hy(const OP1& o1, const OP2& o2, const OP3& o3)
{
return compose_f_gx_hy_t<OP1,OP2, OP3>(o1, o2, o3);
}
output:
除p319,p499有“大小寫無關(guān)”子串查找但未用compose_f_gx_hy。