STL Function Object and Using Lambda

函数对象的概念

重载了运算符()。

1
2
3
4
5
6
class FunctionObjectType {
public:
void operator() () {
// statements
}
}

相比于普通函数有三个优点:1. 可以存储状态。2. 每个函数对象都有自己的类型,可以传递到template里定义特定行为。3. 函数对象一般比函数指针快。

例子: 用函数对象作为排序标准

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#include <iostream>
#include <string>
#include <set>
#include <algorithm>
using namespace std;
class Person
{
public:
string firstname() const;
string lastname() const;
// other stuff
};
// class for 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() || (p1.lastname() == p2.lastname() && p1.firstname() < p2.firstname());
}
};
int main()
{

// create a set with special sorting criterion
set<Person, PersonSortCriterion> coll;
// do sth with the elements
for (auto pos = coll.begin(); pos != coll.end(); ++pos)
{
// ...
}
// ...
}

例子: 有内部状态的函数对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#include <iostream>
#include <string>
#include <set>
#include <list>
#include <algorithm>
#include <iterator>
using namespace std;
class IntSequence
{
private:
int value;
public:
IntSequence(int initialValue): value(initialValue){} // constructor
int operator() () {return ++value;} // "function call"
};
int main()
{

list<int> coll;
// insert values from 1 to 9
generate_n( back_inserter(coll), // start
9, // number of elements
IntSequence(1)); // generates values, starting with 1
copy(coll.cbegin(), coll.cend(), ostream_iterator<int>(cout, " "));
std::cout<<std::endl;
// replace second to last element but one with values starting at 42
generate( next(coll.begin()), // start
prev(coll.end()), // end
IntSequence(42)); // generates values, starting with 42
copy(coll.cbegin(), coll.cend(), ostream_iterator<int>(cout, " "));
std::cout<<std::endl;
system("Pause");
}

如果想传FunctionObject的引用,这样写:

1
2
3
4
5
6
list<int> coll;
IntSequence seq(1); // integral sequence starting with 1
// insert values 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(coll), 4, seq);

例子: for_each()返回自己的函数对象

for_each可以返回自己的function object。就不用像上面那样写得那么费劲。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
// function object to process the mean value
class MeanValue
{
private:
long num; // number of elements
long sum; // sum of all element values
public:
// constructor
MeanValue() : num(0), sum(0){}
// function call
// - process one more element of the sequence
void operator() (int elem)
{

++num; // increment count
sum += elem; // add value
}
// return mean value
double value() {
return static_cast<double>(sum) / static_cast<double>(num);
}
};
int main()
{

int tmp[] = {1,2,3,4,5,6,7,8};
vector<int> coll(tmp, tmp+8);
// process and print mean value
MeanValue mv = for_each(coll.begin(), coll.end(), // range
MeanValue()); // operation
cout << "mean value: " << mv.value() << endl;
system("Pause");
}

谓词 vs 函数对象

Perdicate(谓词)是返回布尔(或可以隐式转换成布尔)值的函数对象。
注意这个标准不足以满足STL的标准:
标准并没有规定遍历时判断谓词会执行多少次。所以谓词的正确执行决不能依赖执行次数,在设计谓词的时候就应该注意这一点。所以谓词不能有状态,应将其()重载定义为const成员函数。

预置的函数对象和Binder

需要包含头文件<functional>

预置的函数对象

其中bit_and, bit_or, bit_xor自从C++11才能用。
Predefined Function Objects
默认用less<>进行排序,所以默认顺序是升序(element < nextElement)。
unordered container默认等价标准是equal_to<>。

函数适配器和Binder

C98那些在C11后已废弃。就不总结了,只看C++11的。
C++11 Predefined Function Adapters
bind()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// bind perdicate
std::transform(coll.begin(), coll.end(), coll.begin(), std::bind(std::plus<int>(), std::placeholders::_1, 10)); // add 10 to each element
auto pos = std::find_if(coll.begin(), coll.end(), std::bind(std::greater<int>(),_1,42)); // find first element > 42
-----------
// bind global function
auto pos = search(s.begin(), s.end(), sub.begin(), sub.end(), bind(equal_to<char>(), bind(my_global_func,_1), bind(my_global_func,_2))); // bind这部分等价于:my_global_func(param1) == my_global_func(param2)
-----------
// bind member function
class MyClass
{
public:
void myfunc() {}
void myfunc2(string& haha){}
}
for_each(coll.begin(), coll.end(), bind(&MyClass::myfunc, _1));
for_each(coll.begin(), coll.end(), bind(&MyClass::myfunc2, _1, "haha: "));
-----------
// bind data members
map<string, int>coll;
int sum = accumulate(coll.begin(), coll.end(), 0, bind(plus<int>(), _1, bind(&map<string,int>::value_type::second, _2)));

mem_fn()
专用于绑定成员函数,可以不要写placeholder了。

1
2
3
std::for_each(coll.begin(), coll.end(), std::mem_fn(&MyClass::myfunc));
std::mem_fn(&MyClass::myfunc)(n); // calls n.myfunc()
std::mem_fn(&MyClass::myfunc2)(n, "Haha: "); // calls n.myfunc2("Haha: ")

not1()和not2()
几乎废弃了。

1
std::sort(coll.begin(), coll.end(), std::not2(std::less<int>()));

使用Lambda

C++11开始有。

Lambda语法

lambda不能是template。
[...] (...) mutable throwSpec -> retType {...}

  • [=]表示外部作用域以值方式传入lambda。
  • [&]表示外部作用域以引用方式传入lambda。

例:

1
2
3
4
5
6
7
8
9
auto l = [] (const std::string& s) {
std::cout << s << std::endl;
};
------------
l("hello lambda");
[]()->double{ return 42;}
------------
int x(0), y(42);
auto qqq = [x, &y] {cout << x; ++y;}

例子:Lambda vs. Binder

1
2
3
4
5
6
7
8
9
10
11
12
13
#include <iostream>
#include <functional>
int main()
{

// lambda way
auto plus10times2 = [] (int i) {
return (i+10)*2;
};
std::cout << "+10 *2: " << plus10times2(7) << std::endl;
// binder way
auto plus10times2_binder = std::bind(std::multiplies<int>(), std::bind(std::plus<int>(), std::placeholders::_1, 10), 2);
std::cout << "+10 *2: " << plus10times2_binder(7) << std::endl;
}

例子:Lambda vs. 有状态的函数对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int main()
{

int temp[] = {1,2,3,4,5,6,7,8};
vector<int> coll(temp, temp+8);
// process and print mean value
long sum = 0;
// 和前文Example: The Return Value of for_each()比较。
// MeanValue mv = for_each (coll.begin(), coll.end(), // range
// MeanValue()); // operation
// cout << "mean value: " << mv.value() << endl;
for_each(coll.begin(), coll.end(),
[&sum] (int elem) {
sum += elem;
});
double mv = static_cast<double>(sum)/static_cast<double>(coll.size());
cout << "mean value: " << mv << endl;
}

[1] The C++ Standard Library 2nd Edition