14.8 函数调用运算符
重载了函数调用运算符的类,它的对象可以做出像函数 一样的行为,因此我们称这样的对象为函数对象。
相对于普通的函数,函数对象可以保存一些状态,这些状态其实就是他的数据成员。
这其实就又和lambda表达式一样了,lambda表达式其实就是一个类的对象。
练习
14.33
零个,一个,多个都可以,而且参数可以有默认值
14.34
struct IfThenElse
{
int operator()(int a,int b,int c) {
return a ? b : c;
};
};
14.35
这里的函数对象是有状态的,保存了is
struct ReadString {
ReadString(istream& _is):is(_is){};
string operator()() {
string temp = "";
getline(is, temp);
return temp;
};
istream& is;
};
14.36
ReadString read_string(cin);
string line;
vector<string> vec;
while ((line=read_string())!="") {
vec.push_back(line);
}
ostream_iterator<string> out_iter(cout,",");
std::copy(vec.begin(), vec.end(), out_iter);
14.37
也可以将target写成数据成员
struct TwoValueEqual
{
bool operator()(const string&str1,const string& target) {
return str1 == target;
};
};
ReadString read_string(cin);
string line;
vector<string> vec;
while ((line = read_string()) != "") {
vec.push_back(line);
}
ostream_iterator<string> out_iter(cout,"\n");
std::copy(vec.begin(), vec.end(), out_iter);
TwoValueEqual func;
auto new_func = std::bind(func, std::placeholders::_1, "123");
//new_func("23");
std::replace_copy_if(vec.begin(), vec.end(), out_iter,new_func,"666");
14.8.1 lambda是函数对象
lambda其实就是一个函数对象,在创建lambda的位置一个匿名类创建了一个函数对象,这个函数对象是const类的。
lambda表达式相当于声明了一个重载了函数调用运算符的类。而且重载的函数是const类型。
下面两个语句基本是等价的。
struct TwoValueEqual
{
bool operator()(const string&str1,const string& target) {
return str1 == target;
};
};
[](const string&str1,const string& target){
return str1 == target;
};
在lambda表达式中,采用值捕获的参数,相当于类中的数据成员,因为是operator()函数是const类型,所以不能修改其值,如果要修改需要将lambda表达式修饰为mutable。使用引用捕获的值为引用类型,可以直接修改。
这在类中也是行得通的,一个非常量引用类型的数据成员在const函数中可以修改其值。
练习
14.38
struct StringEqualSize
{
StringEqualSize(size_t l, size_t u) :_lower(l), _upper(u) {};
bool operator()(const string&str) const {
return (str.size() >= _lower) && (str.size() <= _upper);
};
size_t _lower;
size_t _upper;
};
--测试代码-
ifstream fin("data.txt");
string word;
map<size_t, size_t>word_count;
vector<StringEqualSize> vec = {
StringEqualSize(1, 1),
StringEqualSize(2, 2),
StringEqualSize(3, 3),
StringEqualSize(4, 4),
};
while (fin>>word) {
for (const auto& item:vec)
{
if (item(word))
{
++word_count[word.size()];
break;
}
}
}
for (const auto& item: word_count)
{
cout << item.first<<" appear "<<item.second<<"times"<< endl;
}
14.39
struct StringEqualSize
{
StringEqualSize(size_t l, size_t u,const string& n) :_lower(l), _upper(u),name(n){};
StringEqualSize(size_t l, bool flag,const string& n) :_lower(l), only_lower(flag),name(n) {};
bool operator()(const string&str) const {
bool flag;
if (only_lower)
{
flag = (str.size() >= _lower);
}
else {
flag= (str.size() >= _lower) && (str.size() <= _upper);
}
return flag;
};
size_t _lower;
bool only_lower;
size_t _upper;
string name;
};
---
ifstream fin("data.txt");
string word;
map<string, size_t>word_count;
vector<StringEqualSize> vec = {
StringEqualSize(1u, 9u,"1~9"),
StringEqualSize(10u, true,">=10")
};
while (fin>>word) {
for (const auto& item:vec)
{
if (item(word))
{
++word_count[item.name];
break;
}
}
}
for (const auto& item: word_count)
{
cout << item.first<<" appear "<<item.second<<"times"<< endl;
}
14.40
略
14.41
因为使用lambda表达式更加的快速,不需要太多的定义,如果过去要使用带状态的函数对象需要使用类来定义,这样会很繁琐。
对于一些逻辑简单而且只会出现一次或者小几次的函数可以lambda,需要保存除了形参之外额外的参数,也可以用lambda。
14.8.2 标准库定义的函数对象
标准库为算术运算符、关系运算符、位运算符提供了函数对象,这些函数对象都是模板。。
灵活的利用标准库的函数对象、算法和函数适配器可以简化我们的一部分工作。
14.42
a.
vector<int>vec = { 1231,2312,4123,31,32,4321,543,123,132412 };
auto func = std::bind(std::greater<int>(), std::placeholders::_1, 1024);
auto count = std::count_if(vec.begin(), vec.end(), func);
cout << count << endl;
b.
vector<string> vec = { "pooch","pooch","adsf","pooch","12312" };
auto func = std::bind(std::not_equal_to<string>(), std::placeholders::_1, "pooch");
auto iter = std::find_if(vec.begin(), vec.end(), func);
if (iter!=vec.end())
{
cout << *iter << endl;
}
c.
本想用copy,但是copy的源迭代器和目标迭代器不能是同一个容器中的迭代器
vector<int>vec = { 1231,2312,4123,31,32,4321,543,123,132412 };
auto func = std::bind(std::multiplies<int>(), std::placeholders::_1, 2);
std::transform(vec.begin(), vec.end(),vec.begin(),func);
//vector<int> vec;
for (const auto& item:vec) {
cout<<item<<endl;
}
14.43
使用取模,如果能整除则指为0即false,如果不能整除则值为1,true,所以如果find_if()返回的迭代器为end(),则序列中所有元素都可以整除它。
vector<int>vec = { 2,3,8 };
auto func = std::bind(std::modulus<int>(), 16, std::placeholders::_1);
auto iter = std::find_if(vec.begin(), vec.end(), func);
if (iter==vec.end()) {
cout<<"所有元素都能整除它"<<endl;
}
else {
cout << "有元素不能整除它" << endl;
}
14.8.3 可调用对象与function
对于函数,函数指针、lambda表达式、重载了()的类以及std::bind()他们都属于可调用对象。
这些可调用对象都有自己的类型,但是不同类型的可调用对象可以共享调用形式。
调用形式由返回值类型和实参类型组成。
练习14.44中的所有可调用对象,他们都属于
double(double,double)
调用形式。
我们可以统一的管理这些调用形式一样的函数,比如使用map,但是因为他们的类型是不一样的。
那么怎么确定map的值类型,这个时候可以使用function类型,它可以将不同类型但是相同调用形式的可调用对象都转化为相同的类型。
我觉得本质上是加了一层封装。
这是function类型的一些具体操作。需要在尖括号中传入具体的调用形式。
练习
14.44
double my_minus(double a,double b) {
return a - b;
}
struct my_multi
{
double operator()(double a, double b) {
return a * b;
};
};
struct my_divides
{
double operator()(double a, double b,double c) {
return a / b;
};
};
map<string, std::function<double(double, double)>> caculate;
caculate["+"] = [](double a, double b)->double{return a + b; };
caculate["-"] = my_minus;
caculate["*"] = my_multi();
//caculate["/"] = std::divides<int>();
caculate["/"] = std::bind(my_divides(), std::placeholders::_1, std::placeholders::_2, 0.0);
double a,b;
string oper = "";
while (cin>>a>>oper>>b)
{
cout << a<<oper<<b<<"="<<caculate[oper](a, b) << endl;;
}