1. 程式人生 > 其它 >C++20 之std::function()作為函式輸入輸出引數 tcy

C++20 之std::function()作為函式輸入輸出引數 tcy

技術標籤:C/C++

1.1.說明:
	 C語言用函式指標來作為函式引數實現回撥函式的機制
	 C++中function是通用的多型函式封裝器,它的例項可以儲存、複製以及呼叫任何可以呼叫的目標
	 (包括函式,仿函式,函式指標,bind表示式,lambda閉包)

1.2.function作為函式引數:
	int registerCallBack(std::function<int(*)(int,int)> f);        //基於傳值
	int registerCallBack(const std::function<int(*)(int,int)>& f); //基於引用
	int registerCallBack(const std::function<int(*)(int,int)>&& f);//目前沒有發現有什麼用

	std::function<int(*)(int,int)> fun();
	std::function<int(*)(int,int)>& fun(); //目前沒有發現有什麼用
	std::function<int(*)(int,int)>&& fun();//目前沒有發現有什麼用
1.3.類的成員函式作為函式入參
	//類成員函式預設有隱藏this指標,不能直接作為函式入參
	struct A{
		typedef std::function<void(int i)> callback_t; 
		void foo(callback_t func){f = std::move(func);}//效率更高
		private:    callback_t f;
		};
		
	//類B需註冊一成員函式作為回撥函式到A中
	//用bind實現(顯式傳遞B的this指標作為第一個引數給回撥函式)
	struct B{B(A& a){ a.foo(std::bind(&B::handle, this, std::placeholders::_1));}};

	//使用lambda表示式實現
	struct B{B(A& a){ a.foo([this](int i){B::handle(i);});    

2.例項:

#include <iostream>
#include <functional>
#include<cassert>

using namespace std;

//傳引用:
int foo(int x, int y, const std::function<int(int, int)>& f){	return f(x, y);}

//傳值:
int bar(int x, int y, std::function<int(int, int)> f){	return f(x, y);}

//傳引用:
int fun(int x, int y, std::function<int(int, int)>&& f)
{	auto tmp = std::move(f);return tmp(x, y);}//傳入的是function物件,即使傳入仿函式move也無法移動
int add(int x, int y) { return x + y; }
struct Add {int operator()(int x, int y) { return x + y ; }};

//1.1.function物件用作函式引數:
void test_functon() {
	//1.1.函式指標-普通用法:
	using pfunc = int(*)(int, int);//typedef int(*pfunc)(int, int);等價
	pfunc p = add;assert(p(1, 2) == add(1, 2));
	
	//1.2.函式指標:-輸入引數
	assert(foo(1, 2, p) == add(1,2));

	//2.函式:-輸入引數
	assert(foo(1, 2, add) == add(1, 2));

	//3.仿函式:-輸入引數
	Add add_obj;
	assert(foo(1, 2, add_obj) == add(1, 2));

	//4.lambda:-輸入引數
	auto add_lambda = [](int x, int y)->int {return x + y; };
	assert(foo(1, 2, add_lambda) == add(1, 2));
}
//1.2.測試引數傳值傳引用的區別
//對於函式,labmda,函式指標無區別,但對於有成員變數的仿函式則有區別

struct Add_Inc {
	int operator()(int x, int y) { *z = *z + 1; return x + y; }
	shared_ptr<int> z{ make_shared<int>(10) };
};

void test_fun_input_parameter() {
	assert(foo(1, 2, add) == add(1, 2));    //傳引用
	assert(bar(1, 2, add) == add(1, 2));    //傳值

	Add add_obj;
	assert(fun(1, 2, add_obj) == add(1, 2));//傳move,將仿函式包裝成函式物件fun中move無效
	assert(fun(1, 2, add) == add(1, 2));    //傳move,普通函式fun中move無效

	Add_Inc obj;
	Add_Inc add_inc;

	assert(*add_inc.z == 10);
	
	//當仿函式Add_Inc成員變數z為智慧指標時無論引用傳值都將修改變數z的值;
	//當仿函式Add_Inc成員變數z為int z{10}時無論引用傳值都不修改變數z的值;
	assert(foo(1, 2, add_inc) == add(1, 2)); assert(*add_inc.z == 11);
	assert(bar(1, 2, add_inc) == add(1, 2)); assert(*add_inc.z == 12);
	assert(fun(1, 2, add_inc) == add(1, 2)); assert(*add_inc.z == 13);
}
//1.3.function物件為函式輸出引數
std::function<int(int, int)> add_fun(int x, int y) {
	auto add = [=](int x1,int y1)->int {return x1 + y1 + x + y; };
	return add;
}

std::function<int(int, int)> add_fun_err(int x, int y) {
	auto add = [&x,&y](int x1, int y1)->int {return x1 + y1 + x + y; };
	return add;
}

void test_fun_output_parameter() {
	auto f = add_fun(1, 2);
	assert(f(3, 4) == 1+2+3+4);

	auto f1 = add_fun_err(1, 2);//結果不是你所期望的
	assert(f1(3, 4) == 3 + 4 + 3 + 4);
}
//2.類成員函式作為引數:
class A {
	typedef std::function<int(int, int)> callback_t;
public:
	int add(int x, int y) { return f_add(x, y); }
	void registFunc(callback_t pFunc) { f_add = std::move(pFunc); }
private:
	callback_t f_add;
};
class B {
public:
	int add(int x, int y) { return x + y; }
	B(A& a) { a.registFunc([this](int x, int y) {return B::add(x, y); }); }
};

void test_class() {
	A a;
	B b(a);
	
	assert(b.add(1, 2)==3);//直接使用類成員函式
	assert(a.add(1, 2) == 3);//不夠直觀

	//將類例項物件做一個簡單的包裝,比較容易理解
	auto b_div = [&b](int x, int y) {return b.add(x, y); };
	assert(bar(1, 2, b_div)==3);
}
int main() {
	test_functon();
	test_fun_input_parameter();
	test_fun_output_parameter();
	test_class();
}