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;;
}