Chapter 14 operation overload and type conversion 14.8 function call operator practice and summary

Keywords: Lambda

14.8 function call operators

Overloaded function call operator class, its object can make the same behavior as a function, so we call such an object a function object.

Compared with ordinary functions, function objects can save some states, which are actually their data members.

This is actually the same as lambda expression, which is actually an object of a class.

Practice

14.33

Zero, one and more can be used, and the parameters can have default values

14.34
struct IfThenElse
{

	int operator()(int a,int b,int c) {
		return a ? b : c;
	};
};

14.35

The function object here is stateful, saving 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

You can also write target as a data member

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 is a function object

Lambda is actually a function object. An anonymous class creates a function object where the lambda is created. The function object is const.

A lambda expression is equivalent to declaring a class that overloads function call operators. And overloaded functions are of type const.

The following two statements are basically equivalent.

struct TwoValueEqual
{
	bool operator()(const string&str1,const string& target) {
		return str1 == target;
	};
};

[](const string&str1,const string& target){
	return str1 == target;
};

In lambda expression, parameters captured by value are equivalent to data members in the class. Because operator() function is const type, its value cannot be modified. If you want to modify, you need to modify lambda expression to mutable. The value captured by reference is of reference type and can be modified directly.
This also works in a class. A data member of a non constant reference type can modify its value in the const function.

Practice

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

--Test code-
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

slightly

14.41

Because using lambda expressions is faster and doesn't need too many definitions. If you want to use function objects with state in the past, you need to use classes to define them, which will be very tedious.

For some functions that are simple in logic and only appear once or several times, you can use lambda. You need to save additional parameters in addition to parameters, or you can use lambda.

14.8.2 function object defined by standard library

The standard library provides function objects for arithmetic operators, relational operators, and bitwise operators, all of which are templates..

Flexible use of standard library function objects, algorithms and function adapters can simplify part of our work.

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.

I wanted to use copy, but the source and target iterators of copy cannot be iterators in the same container

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

Using modulus, if integer division is available, it means 0, that is false. If integer division is not available, it means 1, true. Therefore, if the iterator returned by find if() is end(), all elements in the sequence can be integer divided.

	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<<"All elements can divide it"<<endl;
	}
	else {
		cout << "An element cannot be divided" << endl;
	}

14.8.3 callable objects and function s

For functions, function pointers, lambda expressions, overloaded () classes, and std::bind () are all callable objects.

These callable objects all have their own types, but different types of callable objects can share the calling form.

The call form consists of a return value type and an argument type.

All of the callable objects in exercise 14.44 belong to the

double(double,double)

Call form.

We can uniformly manage these functions with the same call form, such as map, but because their types are different.

How to determine the value type of map? function type can be used at this time. It can convert callable objects of different types but the same calling form into the same type.

I think it's essentially a layer of encapsulation.

This is the specific operation of function type. You need to pass in a specific call form in angle brackets.

Practice

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;;
	}
Published 54 original articles, won praise 6, visited 3274
Private letter follow

Posted by vbnullchar on Thu, 27 Feb 2020 20:12:26 -0800