[C++] STL algorithm ③ (Storage state in function object | Value transfer problem when function object is passed as parameter | The function object parameter of for_each algorithm is value transfer)





1. Storing state in function objects



1. Introduction to storage state in function objects


In the C++ language, function objects/functors can be called like functions , and they also have the characteristics of classes. The behavior of overloaded function call operator functions can be modified through inheritance and overloading;

Function objects/functors are usually implemented by defining a class and then overloading the function call operator () for this class;

An important feature of function objects is that they "can store state" ; this means that you can store data in the member variables of the class , and these data can persist between function calls;

Ordinary functions cannot store state , because local variables in ordinary functions are automatically destroyed after the function execution is completed;


A major advantage of function objects/functors is that they can have state , whereas normal functions cannot;

This makes "function objects/functors" very useful in situations where you need to keep some data or state unchanged across multiple function calls.

For example: In STL algorithms, function objects are often used as predicates or functions to perform some operation on each element of the container. Since they can store state, they can be customized according to the needs of the algorithm;

In the following example, a status bit is maintained in the function object to record the number of calls to the function object;


In the function object/functor below, the state n is stored. Each time the functor is called, this member will increase by 1;

//函数对象 类重载了()
template <typename T>
class PrintT {
    
    
public:
	void operator()(T& t) {
    
    
		cout << n << " . " << t << endl;
		// 每调用一次, 自增 1
		n++;
	}

private:
	// 每调用一次, 该成员自增 1
	// 该状态一直存储
	int n = 0;
};

2. Example analysis


In the code example below,

First, the function object/functor PrintT class is defined, which overloads the function call operator (), and its overloaded function is void operator()(T& t);

  • In this function object, a state value n is stored,
  • Each time this overloaded function is called, the status value n will increase by 1;
//函数对象 类重载了()
template <typename T>
class PrintT {
    
    
public:
	void operator()(T& t) {
    
    
		cout << n << " . " << t << endl;
		// 每调用一次, 自增 1
		n++;
	}

private:
	// 每调用一次, 该成员自增 1
	// 该状态一直存储
	int n = 0;
};

Then, in the foreach loop, the function object is passed into the loop algorithm. Each time the elements in the vector container are traversed, the function object will be called. At the same time, each time it is called, the n value in the function object will increase by 1;

	// 向 foreach 循环中传入函数对象
	// 在函数对象中打印元素内容
	for_each(vec.begin(), vec.end(), PrintT<int>());

Code example:

#include "iostream"
using namespace std;
#include <vector>
#include <algorithm>
#include "functional"

//函数对象 类重载了()
template <typename T>
class PrintT {
    
    
public:
	void operator()(T& t) {
    
    
		cout << n << " . " << t << endl;
		// 每调用一次, 自增 1
		n++;
	}

private:
	// 每调用一次, 该成员自增 1
	// 该状态一直存储
	int n = 0;
};

int main() {
    
    

	// 创建一个 vector 单端数组容器
	vector<int> vec;

	// 向容器中插入元素
	vec.push_back(1);
	vec.push_back(3);
	vec.push_back(5);

	// 向 foreach 循环中传入函数对象
	// 在函数对象中打印元素内容
	for_each(vec.begin(), vec.end(), PrintT<int>());


	// 控制台暂停 , 按任意键继续向后执行
	system("pause");
	return 0;
};

Execution result: When printing, first print out the status value n, and then print out the elements in the vector container.

0 . 1
1 . 3
2 . 5Please
press any key to continue. . .

Insert image description here





2. Value transfer problem when function objects are passed as parameters



1. The function object parameter of the for_each algorithm is passed by value


Let’s start analyzing the specific details of the function object as a parameter in the for_each function;

The calling code of the for_each algorithm is as follows:

	// 向 foreach 循环中传入函数对象
	// 在函数对象中打印元素内容
	for_each(vec.begin(), vec.end(), PrintT<int>());

The function prototype of the for_each algorithm is as follows:

// FUNCTION TEMPLATE for_each
template <class _InIt, class _Fn>
_Fn for_each(_InIt _First, _InIt _Last, _Fn _Func) {
    
     // perform function for each element [_First, _Last)
    _Adl_verify_range(_First, _Last);
    auto _UFirst      = _Get_unwrapped(_First);
    const auto _ULast = _Get_unwrapped(_Last);
    for (; _UFirst != _ULast; ++_UFirst) {
    
    
        _Func(*_UFirst);
    }

    return _Func;
}

The formal parameter _Fn _Func of the above for_each function is a value, not a reference;

If a reference is passed , the external object and the actual parameter value are the same object;

If a value is passed , the actual parameter is just a copy of the external object. In the for_each function, no matter how the actual parameter is changed, it will not affect the external object;


If a function object is called in the for_each algorithm, there is a state change in the function object;

Continue to call the function object outside the for_each algorithm. Since for_each is a value transfer, only a copy of the function object is passed, and the status change of the copy will not affect the external function;

If you want to retain the above state changes, you need to use a function object to receive the return value of for_each. This function object retains the state value of the internal function object parameter copy;


2. Code example - the function object parameters of the for_each function do not retain state externally


If a function object is called in the for_each algorithm, there is a state change in the function object;

Continue to call the function object outside the for_each algorithm. Since for_each is passed by value, only a copy of the function object is passed. The state change of the copy will not affect the external function;


When calling the function object externally, it is found that the status value is still 0, which means that the value transfer changes the copy value of the actual parameter of the function object and does not affect the external function object value;

0 . 666

Code example:

#include "iostream"
using namespace std;
#include <vector>
#include <algorithm>
#include "functional"

//函数对象 类重载了()
template <typename T>
class PrintT {
    
    
public:
	void operator()(T& t) {
    
    
		cout << n << " . " << t << endl;
		// 每调用一次, 自增 1
		n++;
	}

private:
	// 每调用一次, 该成员自增 1
	// 该状态一直存储
	int n = 0;
};

int main() {
    
    

	// 创建一个 vector 单端数组容器
	vector<int> vec;

	// 向容器中插入元素
	vec.push_back(1);
	vec.push_back(3);
	vec.push_back(5);


	// 创建函数对象
	PrintT<int> printT;

	// 向 foreach 循环中传入函数对象
	// 在函数对象中打印元素内容
	for_each(vec.begin(), vec.end(), printT);

	// 再次调用 函数对象
	cout << "再次调用函数对象 : " << endl;
	int a = 666;
	printT(a);


	// 控制台暂停 , 按任意键继续向后执行
	system("pause");
	return 0;
};

Results of the :

0 . 1
1 . 3
2 . 5
再次调用函数对象 :
0 . 666
请按任意键继续. . .

Insert image description here


3. Code example - function object return value of for_each function


If a function object is called in the for_each algorithm, there is a state change in the function object;

Continue to call the function object outside the for_each algorithm. Since for_each is passed by value, only a copy of the function object is passed. The state change of the copy will not affect the external function;

If you want to retain the above state changes, you need to use a function object to receive the return value of for_each. This function object retains the state value of the internal function object parameter copy;


Use PrintT<int> printT;the function object variable to receive the return value of the for_each algorithm, execute the function object call again, and find that the status value is retained, and the printed value is:

3 . 666

Code example:

#include "iostream"
using namespace std;
#include <vector>
#include <algorithm>
#include "functional"

//函数对象 类重载了()
template <typename T>
class PrintT {
    
    
public:
	void operator()(T& t) {
    
    
		cout << n << " . " << t << endl;
		// 每调用一次, 自增 1
		n++;
	}

private:
	// 每调用一次, 该成员自增 1
	// 该状态一直存储
	int n = 0;
};

int main() {
    
    

	// 创建一个 vector 单端数组容器
	vector<int> vec;

	// 向容器中插入元素
	vec.push_back(1);
	vec.push_back(3);
	vec.push_back(5);


	// 创建函数对象
	PrintT<int> printT;

	// 向 foreach 循环中传入函数对象
	// 在函数对象中打印元素内容
	printT = for_each(vec.begin(), vec.end(), printT);

	// 再次调用 函数对象
	cout << "再次调用函数对象 : " << endl;
	int a = 666;
	printT(a);


	// 控制台暂停 , 按任意键继续向后执行
	system("pause");
	return 0;
};

Results of the :

0 . 1
1 . 3
2 . 5
再次调用函数对象 :
3 . 666
请按任意键继续. . .

Insert image description here

Supongo que te gusta

Origin blog.csdn.net/han1202012/article/details/135421912
Recomendado
Clasificación