`

C++11笔记四

 
阅读更多

一,函数对象

    函数对象相比于函数的好处,是:

    1)类或结构体中可以有自己的成员变量;

    2)对于标准模版库STL算法中,比如配合std::for_each/find_if/sort/partition/remove_if等,使用方便;

#include <iostream>
#include <string> 

using namespace std;

template <typename T>
int funcAdd(T t1, T t2) {
	return t1 + t2;
}

template <typename T>
struct classAdd {

	// 函数对象必须是实现operator(),参数和返回值可以自定
	// struct和class一样,如果是class需要声明public
	int operator() (const T t1, const T t2)  const
	{
		return t1 + t2;
	}
};
  
int main()
{ 
	int funcSum = funcAdd(1, 2);
	string result = "func result:" + to_string(funcSum);
	cout << result.c_str() << endl;
	
	auto obj = classAdd<int>(); // 函数对象
	int classSum = obj(1, 2); // 也可以写成:classAdd<int>()(a1, a2)
	result = "class result:" + to_string(classSum);
	cout << result.c_str() << endl;
	 
	system("pause");
	return 0;
}

 

二,lambda表达式

    lambda表达式被视为包含公有operator()的匿名结构体(或类)。从这种意义上说,lambda表达式属于函数对象。所以lambda表达式也叫lambda函数。

        auto lambda = [](const int a, const int b) {return a + b; };
	int lambdaSum = lambda(1, 2);
	result = "lambda result:" + to_string(lambdaSum);
	cout << result.c_str() << endl;

 

        int base = 5;
	auto lambda = [base](const int a, const int b) {return a + b + base; };
	int lambdaSum = lambda(1, 2); // result: 8
	result = "lambda result:" + to_string(lambdaSum);
	cout << result.c_str() << endl;

 

	int base = 5;
	auto lambda = [base](const int a, const int b) mutable {
		base = 10; // 没有mutable关键字,这里报错,但其实外部的值还是没有改变
		return a + b + base; 
	};
	lambda(1, 2);  
	cout << "base1:" << base << endl; // print 5

	auto lambda2 = [&base](const int a, const int b) {
		base = 20; // 可以修改
		return a + b + base;
	};
	lambda2(1, 2);
	cout << "base2:" << base << endl; // print 20

 

	int base = 5;
	auto lambda = [=](const int a, const int b) mutable -> int // =号声明可以使用外部所有变量
	{
		base = 10; // 但不允许修改
		return a + b + base;
	};
	int sum = lambda(1, 2);
	cout << "base:" << base << endl; // print 5
	cout << "sum:" << sum << endl; // print 8

    lambda的[] 中,除了具体的变量a或引用&a,可以使用:=,&,this等:

    1)=:lambda可以使用,所在函数体内所有可见的局部变量(包括所在类的this),按值传递方式。

    2)&:lamdda可以使用,所在函数体内所有可见的局部变量(包括所在类的this),按引用传递方式。

    3)this:lamdda可以使用,所在所在类的成员变量;

    4)空:没有任何对象参数;

	auto lambda = [](const int a, const int b) -> int // ->ReturnType 明确指明返回类型
	{
		return a + b; 
	};
	int sum = lambda(1, 2);  
	cout << "sum:" << sum << endl; // print 3

    lambda表达式应该尽量简短,跨越多行调用lambda表达式可能无助于提高编程效率,此时应使用易于重用的函数对象。 

 

 三,std::bitset,用于处理位和位标志的STL类,不允许调整长度。

#include <iostream>
#include <string> 
#include <bitset>

using namespace std;
 
int main()
{  
	bitset<5> fiveBits;
	cout << fiveBits << endl; // print: 00000

	bitset<4> fourBits(5);//或fourBits("0101")或fourBits(0b0101) 
	cout << fourBits << endl; // print: 0101
	cout << fourBits[2] << endl; // 获取第三位,print: 1

	bitset<4> fourBits2 = fourBits << (2); // 左移两位
	cout << fourBits2 << endl; // print: 0100

	bitset<4> result(fourBits & fourBits2); // 执行&
	cout << result << endl; // print: 0100

	fourBits.set(); // 全部置1
	cout << fourBits << endl; // print: 1111

	fourBits.set(1, 0); // 第二位置0
	cout << fourBits << endl; // print: 1101

	fourBits.reset(0); // 第一位置0
	cout << fourBits << endl; // print: 1100

	fourBits.reset(); // 全部置0
	cout << fourBits << endl; // print: 0000

	fourBits.flip(); // 取反
	cout << fourBits << endl; // print: 1111

	cout << fourBits.size() << endl; // 返回位数

	cout << fourBits.count() << endl; // 返回为1的数量

	system("pause");
	return 0;
}

 

 四,自定义异常

    1,注意throw字符串的捕获;

    2,注意自定义异常继承时,声明public的区别;

#include <iostream>
#include <string> 
#include <exception>

using namespace std;

class MyException : public std::exception // 如果是public继承,则catch (const std::exception& e)可以捕获
{
	string reason;

public:
	MyException(const char* why) :reason(why) {

	}

	virtual const char* what() const throw() {
		return reason.c_str();
	}
};

void test1() {
	throw "exception test..."; // catch (const char* errorMsg)捕获
}

void test2() {
	throw MyException("MyException"); // throw 自定义异常
}
 
int main()
{   
	try
	{
		//test1();
		test2();
	}
	catch (MyException& e) // 捕获自定义异常
	{
		cout << "MyException:" << e.what() << endl;
	}
	catch (const std::exception& e)
	{
		cout << "std::exception:" << e.what() << endl;
	}
	catch (const char* errorMsg) // 捕获throw出来的字符串
	{
		cout << "errorMsg:" << errorMsg << endl;
	}
	system("pause");
	return 0;
}

 

五,nullptr和NULL

    nullptr的出现是为了替代NULL。在某种意义上说,传统C++会把NULL、0视为同一种东西,这取决于编译器如何定义NULL。有些编译器会将NULL定义为((void*)0),而有些则会直接将其定义为0。

    C++不允许直接将void*隐式转换到其它类型,但如果NULL被定义为((void*)0),那么当编译:

char *ch = NULL;

    时,NULL只好被定义为0。而这依然会产生问题,将导致C++中重载发生混乱。

void foo(char*)
void foo(int);

    例如对于这两个函数,如果NULL被定义为0,那么foo(NULL)将调用foo(int),从而导致代码违反直观。

 

    为了解决这个问题,c++11引入了nullptr关键字,专门用于区分空指针和0。nullptr的类型为nullptr_t,能够隐式地转换为任何指针或成员指针的类型,也能和他们进行相等或不等的比较。

#include <iostream>
#include <string>  

using namespace std;
 

void foo(int i) {
	cout << "foo(int)" << endl;
}

void foo(char* c) {
	cout << "foo(char*)" << endl;
}
 
int main()
{   
	foo(1); // print foo(int)

	foo(NULL); // print foo(int)

	foo(nullptr); // print foo(char*) 

	system("pause");
	return 0;
}

 

 六,decltype关键字

    decltype是为了解决auto只能对变量进行类型推导的缺陷而出现。decltype根据表达式来推倒类型。

decltype(表达式)

    在函数声明中,与auto配合来推导函数返回类型:

#include <iostream>
#include <string>  

using namespace std;
 

template <typename T, typename U>
auto add(T t, U u) ->decltype(t+u) // decltype用于智能推导表达式(t+u)的类型 
{ 
	return t + u;
}
 
int main()
{   
	auto result = add(1, 5.1); 
	cout << result << endl;

	auto a1 = 2;
	auto a2 = 2.5;

	decltype(a1*a2) r1 = a1 + a2; // decltype用于智能推导表达式(a1*a2)的类型
	cout << r1 << endl; // print 3.5

	decltype(a1) r2 = a1 + a2; // decltype用于智能推导表达式(a1)的类型
	cout << r2 << endl; // print 3.5 

	system("pause");
	return 0;
} 

    在C++14中不用声明使用->decltype来推导函数返回类型。我手头上的C++11也行。

template <typename T, typename U>
auto add(T t, U u) 
{ 
	return t + u;
}

    推导规则:decltype(exp)

    1)exp是标识符、类访问表达式,decltype(exp)和exp的类型一致;

    2)exp是函数调用,decltype(exp)和返回值的类型一致;

    3)其它情况,若exp是一个左值,则decltype(exp)是exp类型的左值引用,否则和exp类型一致。

    decltype也经常用在通过变量表达式抽取变量类型上:

 

vector<int> v;
//......
decltype(v)::value_type i = 0; // 只要知道v是一个容器就可以了。

 

 

七,外部模版

    在传统C++中,模板只有在使用时才会被编译器实例化。即:只要在每个编译单元(文件)中编译的代码中,遇到被完整定义的模板,都会实例化。这就产生了重复实例化而导致的编译时间增长。而且我们没有办法通知编译器不要进行模版实例化。

    C++引入了外部模版,扩充了原来的强制编译器在特定位置实例化模板的语法,使得能够显示地告诉编译器何时进行模板的实例化。

template class std::vector<MagicClass>; //强行实例化
extern template class std::vector<MagicClass>; // 不在该编译文件中实例化模板

 

八,类型别名模板

    在了解类型别名模板之前,需要理解“模板”和“类型”之间的不同:模板是用来产生类型的。

    在传统C++中,typedef可以为类型定义一个新的名称,但无法为模板定义一个新的名称。因为模板不是类型

template <typename T, typename U>
class SuckType;

typedef SuckType<int, std::string> NewType; // 不合法

    C++11中使用using来支持为模板定义一个新的名称,同时支持传统typedef的相同功效。

    通常我们使用typedef定义别名的语法是:“typedef 原名称 新名称;”,但对函数指针等别名的定义语法却不相同,通常会给阅读造成一定程度的困难。

typedef int (*process)(void*); // 定义一个返回类型为int,参数为void)的函数指针类型,名称叫做process

using process = int(*)(void *); // 同上,更加直观
using NewType = SuckType<int, std::string>;

    使用using配合模版定义:

void add(int a, int b) {
	cout << "add" << endl;
}
template <typename T>
using MyFunc = void(*)(T t, T u); // 只是定义了一个别名

int main(int, char *[])
{      

	MyFunc<int> test;
	test = add;
	test(1, 2);

	system("pause");
	return 0;
}

    也可以通过using定义任意类型的表达式:

template <typename T>
using type_t = T;

type_t<int> i = 10; // type_t<int>就是int

    声明嵌套类型:

class MyClass
{
public:
	typedef int C; // 声明嵌套类型
	using B = int; // 声明嵌套类型
};

int main(int, char *[])
{       
	MyClass::C i1 = 10;
	cout << typeid(i1).name() << endl; // int

	MyClass::B i2 = 20;
	cout << typeid(i2).name() << endl; // int


	system("pause");
	return 0;
}

  

 

九,typedef和using对函数指针的别名使用

#include <iostream>
#include <string>  

using namespace std;
 
template <typename T, typename U>
class MyClass;

typedef int func_t_t(int, int);
typedef int(*func_t_p)(int, int);
typedef int(&func_t_r)(int, int);


using func_u_t = int(int, int);
using func_u_p = int(*)(int, int);
using func_u_r = int(&)(int, int);

int sum(int a, int b)
{
	return a + b;
}

int sub(int a, int b)
{
	return a - b;
}

func_t_p calc_t_p(char type) {
	if (type == '+') {
		return sum;
	}
	return sub;
}
func_u_p calc_t_r(char type) {
	if (type == '+') {
		return sum;
	}
	return sub;
}
func_t_p calc_u_p(char type) {
	if (type == '+') {
		return sum;
	}
	return sub;
}
func_u_r calc_u_r(char type) {
	if (type == '+') {
		return sum;
	}
	return sub;
}

int calc_t(char type, int a, int b) {
	func_t_p tp = calc_t_p(type);
	return tp(a, b);
}

int main(int, char *[])
{
	func_t_t * f_t_t = sum;
	func_t_p   f_t_p = sum;
	func_t_r   f_t_r = sum;

	func_u_t * f_u_t = sum;
	func_u_p   f_u_p = sum;
	func_u_r   f_u_r = sum;

	std::cout << f_t_t(1, 2) << std::endl;
	std::cout << f_t_p(1, 2) << std::endl;
	std::cout << f_t_r(1, 2) << std::endl;

	std::cout << f_u_t(1, 2) << std::endl;
	std::cout << f_u_p(1, 2) << std::endl;
	std::cout << f_u_r(1, 2) << std::endl;
	 
	std::cout << calc_t_p('-')(1, 2) << std::endl;
	std::cout << calc_t_r('-')(1, 2) << std::endl;
	std::cout << calc_u_p('-')(1, 2) << std::endl;
	std::cout << calc_u_r('-')(1, 2) << std::endl;

	std::cout << calc_t('-', 1, 2) << std::endl;

	system("pause");
	return 0;
}

 

十,typdef用于结构体struct的区别

struct TagStruct // TagStruct是标记,在声明变量时使用,可以不存在
{ 
	int age;
} aaa; //变量

typedef struct TagStruct2  // TagStruct2是标记,在声明变量时使用,可以不存在
{ 
	int age;
} BBB; //BBB是类型名称,可以在变量声明时可以替代struct TagStruct2

aaa.age = 10;
cout << "aaa.age:" << aaa.age << endl;

struct TagStruct ccc; // 通过struct+标记,声明变量
ccc.age = 15;
cout << "ccc.age:" << ccc.age << endl;

BBB bbb; 
bbb.age = 20;
cout << "bbb.age:" << bbb.age << endl;

struct TagStruct2 ddd;
ddd.age = 30;
cout << "bbb.age:" << ddd.age << endl;

  

十一,->*与.*的示例

class MyClass
{
private:
	int age;

public:
	MyClass(int a) :age(a) {

	}
	int GetAge() {
		return age;
	}
	int AddAge(int a) {
		return this->age + a;
	}

};


int main(int, char *[])
{    
	MyClass item(10);
	int temp = item.AddAge(5);
	cout << temp << endl; // print: 15


	int (MyClass::*MyAdd)(int) = &MyClass::AddAge; // 指向成员的指针用于调用函数

	temp = (item.*MyAdd)(2);
	cout << temp << endl; // print: 12

	MyClass *newItem = &item;
	temp = (newItem->*MyAdd)(8);
	cout << temp << endl; // print: 18
	 
	system("pause");
	return 0;
}

 

十二,类的隐藏、覆盖和重载,using关键字使用

    类的隐藏和重载不一样,

    1、类的隐藏是指,一个类继承自另外一个类,则父类中与子类:

        1)名称相同的函数,参数不同,将被隐藏,不管父类中这个函数重载了几次,子类中将不能再使用它们。

        2)名称相同的函数,参数相同,但没有virtual关键字,也会被隐藏;有virtual关键字是覆盖。

    2、类的覆盖是指,一个类继承自另外一个类,则父类中与子类名称相同、且参数相同、且父类函数必须声明virtual关键字;

    3、类的重载是指,同一个类中,函数名称相同,参数不同,virtual关键字可有可无,的多个函数。

class Base
{
public:
	void printTest()
	{
		cout << "printTest..."<< endl;
	}
	void printTest(int i)
	{
		cout << "printTest int." << i << endl;
	}
	void printTest(double i)
	{
		cout << "printTest double." << i << endl;
	}
	void printABC() {
		cout << "printABC..." << endl;
	}

private:
	void printDEF() {
		cout << "printABC..." << endl;
	}
};

class Sub :Base
{
public:
	using Base::printTest;
	void printTest(string s)
	{
		cout << "printTest string." << s.c_str() << endl;
	}
};

class Sub2 :private Base
{
public:
	using Base::printTest;
};

void test() { 

	Sub sub;
	sub.printTest(2); // 父类printTest(int i)被隐藏,使用using Base::printTest;可以访问

	Sub2 sub2;
	sub2.printTest(2); // 私有继承,无法访问父类函数,使用using Base::printTest;可以访问
	sub2.printTest(2.4);
	//sub2.printABC(); // 无法访问,没有使用using在子类中声明
	//sub2.printDEF(); // 使用using也无法访问私有函数
}

int main(int, char *[])
{     
	test(); 
	system("pause");
	return 0;
}

    使用using可以访问private继承时父类非private函数。

 

十三,typename的特定用法

    在模版定义中,typename与class相同。但以下用途是为了避免歧义:

template <class T>
void foo() 
{
    typename T::iterator *iter; // 避免T中包含iterator静态变量引起的歧义。
}

    这里typename声明T::iterator是一种类型,而不是变量。编译器在编译时就确定。否则可能好会被当成T::iterator*iter乘法表达式。使用typename的规则:

    1)模板之外禁止使用,即typename只能用于模板定义中;

    2)非限定类型禁止使用,比如int,vector<int>之类;

    3)基类列表中禁止使用,比如template<class T> class C1: T::InnerType不能在T::InnerType前面加typename;

    4)构造函数的初始化列表中禁止使用。

    5)如果类型依赖于模型参数的限定名,那么在它之前就必须加typename(除非是基类列表,或者在类的初始化成员列表中)。

    6)其它情况下typename是可选的,对于一个不依赖于名的限定名,typename可选。例如vector<int> vi;

    7)对于不会引起歧义的地方,仍然需要加typename修饰;

 

template <class T>
void foo() 
{
    typename T::iterator iter; // 避免T中包含iterator静态变量引起的歧义。
    typedef typename T::iterator iterator_type; // 无歧义地声明嵌套类型
}
 

十四,使用std::initializer_list初始化列表

    std::initializer_list是非常高效的,作为传参并不会耗费额外的性能。但我们应当总是把std::initializer_list看做保存对象的引用,并在它持有对象的生存周期结束前完成传递。另外std::initializer_list中的元素是只读的,不可修改。

#include <iostream>
#include <string>  
#include <vector>
#include <map>

class FooVector
{
	std::vector<int> pool;
public:
	FooVector(std::initializer_list<int> list)
	{
		for (auto it = list.begin(); it != list.end(); ++it)
		{
			pool.push_back(*it);
		}
	}
};
class FooMap
{
	std::map<std::string, int> pool;
	using pair_t = std::map<std::string, int>::value_type;
public:
	FooMap(std::initializer_list<pair_t> list)
	{
		for (auto it = list.begin(); it != list.end(); ++it)
		{
			pool.insert(*it);
		}
	}
};

void func(std::initializer_list<int> list)
{
	std::cout << "size:" << list.size() << std::endl;
	for (auto it = list.begin(); it != list.end(); ++it)
	{
		std::cout << *it << std::endl;
	}
}

// 不能将initializer_list作为返回值,因为它只保留了引用
std::initializer_list<int> getInitList() 
{
	int a = 1;
	int b = 2;
	return{a, b}; // 没有复制值
}

std::vector<int> getVector() 
{
	int a = 1;
	int b = 2;
	return{ a, b };
}

template <typename T>
class Age
{
	T age;
public:
	Age(T i)
	{
		age = i;
	}
	T getAge()
	{
		return age;
	}
};
template <typename T>
class AgePool
{
	std::vector<T> pool;
public:
	AgePool(std::initializer_list<T> list)
	{
		for (auto it = list.begin(); it != list.end(); ++it)
		{
			pool.push_back(*it);
		}
	}
	void print()
	{
		for (auto t : pool)
		{
			std::cout << "age:" << t.getAge() << std::endl;
		}
	}
};

void print()
{

}

int main(int, char *[])
{       
	FooVector fv{1, 2, 3, 4};
	FooMap fm{ {"a", 1}, {"b", 2} };

	func({1, 2, 3, 4, 5, 6});

	auto a1 = getInitList();
	std::cout << "getInitList size:" << a1.size() << std::endl; // print 2
	for (auto it = a1.begin(); it != a1.end(); ++it)
	{
		std::cout << "getInitList:" << *it << std::endl; // 任意值
	}

	auto a2 = getVector();
	std::cout << "getVector size:" << a2.size() << std::endl; // print 2
	for (auto it = a2.begin(); it != a2.end(); ++it)
	{
		std::cout << "getVector:" << *it << std::endl; // print 1, 2
	}

	AgePool<Age<double>> ap{1, 2, 3, 4, 5}; // 批量Age对象初始化
	ap.print();
	 
	system("pause");
	return 0;
}

 

十五,使用std::for_each

   头文件:#include <algorithm>

#include <iostream>
#include <string>  
#include <vector>
#include <map>
#include <algorithm>
#include <memory>
 
using namespace std;

void print(int i)
{
	cout << i << endl;
}
 
void test() 
{
	vector<int> v{ 1, 2, 3, 4, 5 };
	std::for_each(v.begin(), v.end(), print);
	 
	auto func = [](int i) {
		cout << i << endl;
	};
	std::for_each(v.begin(), v.end(), func); 
}



int main(int, char *[])
{       
	test();

	system("pause");
	return 0;
}

 

十六,逗号表达式

    以括号为一个返回值的逗号表达式,会逐个执行,但只返回最后一个的值。

	int a = (5, 6);
	cout << "a:" << a << endl; // print 6

	int r = (a * 3, a * 4);
	cout << "r:" << r << endl; // print 24

	int i1 = 0;
	int r2 = (++i1, 3, 5); // 会执行i1++
	cout << "i1:" << i1 << endl; // print 1
	cout << "r2:" << r2 << endl; // print 5

	int i2 = ([](int x, int y) {
		return x+y; 
	}(1, 2), 5, 6); // 第一个lambda执行,但只赋值最后一个
	cout << "i2:" << i2 << endl; // print 6

	int i3 = ((6, 7, 8), 9); // 第一个内嵌括号返回8,加上外部括号,返回9
	cout << "i3:" << i3 << endl; // print 9

	string i4 = ([] {return 1; }, "abc"); // 第一个只是lambda函数定义,没有执行
	cout << "i4:" << i4.c_str() << endl; // print abc
	 
	auto list1 = std::initializer_list<int>{ [&]() { 
		return 1;
	}(), [&](int i) {
		return i;
	}(2), 3, 4}; // 第一、二个lambda执行
	for (auto aa : list1) {
		cout << "list1-elem:" << aa << endl; // print 1 2 3 4
	}

	int val = 2;
	auto list2 = std::initializer_list<int>{ ([&]() {
		cout << "lam val:" << val << endl; // no-print
		return val;
	}(), 6, 9), 7 };
	for (auto aa : list2) {
		cout << "list2-elem:" << aa << endl; // print 9 7
	} 

	auto list4 = { []() {
		return 2;
	}(), 1 };
	for (auto aa : list4) {
		cout << "list4-elem:" << aa << endl; // print 2 1
	}

  

十七,可变模版遍历

#include <iostream>
#include <string>  
#include <vector>
#include <map>
#include <algorithm>
#include <memory>
 
using namespace std;

template <typename T>
void printf(T t)
{
	cout << "printT:"<< t << endl;
} 
template <typename T, typename...Args>
void printf(T t, Args... args)
{
	cout << "printArgsSize:" << sizeof...(args) << endl;  
	printf(args...); // 当args只有一个参数时,调用printf(T t);即跳出回调
}

template <typename T, typename...Args>
void printfList(T t, Args... args)
{
	cout << "printf2:" << t << endl; 

	// 利用逗号表达式,最后一项t为std::initializer_list<T>中T的推导,利用...执行多次,最后list为两个t值。所以list没用,重要的是利用这种方式遍历args...
	// T与Args可能不同类型
	auto list = std::initializer_list<T>{([&] {
		cout << "lambda:" << args << endl;
	}(), t)...}; 
	for (auto a : list)
	{
		cout << "list:" << a << endl;
	}
}
 
class MyWork
{
private:
	int base;
public: 
	MyWork(int i):base(i)  { 
	}

	void print()
	{
		cout << base << endl;
	}
};

template <typename T, typename...Args>
void testWork(T t, Args... args)
{
	// T和Args都是相同类型
	auto list = std::initializer_list<T>{t, ([&] { 
		return args;
	}())... }; // 返回work1 work2 work3的集合
	for (auto a : list)
	{
		a.print();
	}
}

int main(int, char *[])
{         

	printf(1, "abc", 3.5);

	printfList(2, "abc", 3.5);

	testWork<MyWork>(MyWork(1), MyWork(2), MyWork(3)); 

	system("pause");
	return 0;
}

  

十八,可调用对象及包装器std::function

    可调用对象定义:

    1)是一个函数指针;

    2)是一个具有operator()成员函数的类对象(仿函数);

    3)是一个可以被转换为函数指针的类对象;

    4)是一个类成员(函数)的指针;

#include <iostream>
#include <string>  
#include <vector>
#include <map>
#include <algorithm>
#include <memory>
 
using namespace std;

void func()
{
	// ...
}
struct Foo
{
	void operator()(void) // 仿函数
	{
		// ...
	}
};
struct Bar
{
	static void func()
	{
		// ...
	}
	using fr_t = void(*)(void);
	operator fr_t(void)
	{
		return func; // 可被转换为函数指针的类对象
	}
};
struct A
{
	int a_;
	void mem_func() // 类成员
	{
		//... 
	}
};

int main(int, char *[])
{  
	void(*func_ptr)(void) = &func; // 函数指针
	func_ptr();

	Foo foo;
	foo(); // 仿函数

	Bar bar;
	bar(); // 可被转换为函数指针的类对象

	void (A::*mem_func_ptr)(void) = &A::mem_func; // 类成员函数指针
	int A::*mem_func_num = &A::a_; // 类成员指针

	A aa;
	(aa.*mem_func_ptr)(); // 执行函数
	aa.*mem_func_num = 13; // 给成员变量赋值
	 
	system("pause");
	return 0;
}

    对上述可调用类型的定义,并没有包括函数类型或函数引用(只有函数指针),这是因为函数类型并不能直接用于定义对象,而函数引用从某种意义上来说,可以看做是一个const的函数指针。 

    std::function是可调用对象的包装器。它是一个类模板,可以容纳类除了成员(函数)指针以外的所有可调用对象。通过指定它的模板参数,从而用统一的方式处理函数、函数对象、函数指针,并允许保存或延迟执行它们。当给std::function合适的函数签名(即一个函数类型,只需要包括返回值类型和参数表类型),它就可以变成一个可以容纳所有这一类调用方式的“函数包装器”。

#include <iostream>
#include <string>   
#include <functional>
 
using namespace std;

void func()
{
	cout << __FUNCTION__ << endl;
}
struct Foo
{
	int operator()(void) 
	{
		cout << __FUNCTION__ << endl;
		return 0;
	}
};
struct Bar
{
	static int func(int a)
	{
		cout << __FUNCTION__ << endl;
		return 0;
	}
};

class A
{
	std::function<void()> callback_; // 封装回调函数
public:
	A(const std::function<void()>& f):callback_(f){}

	void callback()
	{
		callback_();
	}

	int a_;
	int addOne(int i)
	{
		return i + 1;
	}
};

void call_func(int x, const std::function<void(int)> f) // 作为参数传递
{
	f(x);
}
void print(int a)
{
	cout << "print:" << a << endl;
}

int main(int, char *[])
{  
	std::function<void(void)> fr1 = func; // 函数指针
	fr1();

	std::function<int(int)> fr2 = Bar::func; // 类的静态函数
	int i = fr2(2);

	Foo foo;
	std::function<void(void)> fr3 = foo; // 仿函数
	fr3();
	
	A a(func);
	a.callback(); // 任何时候触发回调

	call_func(4, print); // 传递函数指针

	// lambda 
	call_func(7, [](int i) {
		cout << "lambda:" << i << endl;
	}); 

	// 使用bind来绑定函数和参数,就不需要function那样声明函数签名
	auto bind1 = std::bind(print, std::placeholders::_1);
	call_func(5, bind1);

	auto bind2 = std::bind(print, std::placeholders::_1);
	bind2(10);

	auto bind3 = std::bind(print, 20);
	bind3();

	// function+bind用于绑定类对象的非静态函数
	std::function<int(int)> fb1 = std::bind(&A::addOne, &a, std::placeholders::_1);
	int r = fb1(1);
	cout << "r:" << r << endl; // print 2

	// function+bind用于绑定类对象的成员变量
	std::function<int&(void)> fb2 = std::bind(&A::a_, &a);
	fb2() = 123;
	cout << "a:" << a.a_ << endl; // print 123

	system("pause");
	return 0;
}

 

十九,tuple元组及遍历

#include <iostream>
#include <string>   
#include <tuple>
 
using namespace std;
 
struct A
{
	int a;
	char* p;
	double b;
};

template <typename T, int N> // 模板中可以定义变量
struct tuple_print
{
	static void print(const T& t, std::ostream& os)
	{ 
		tuple_print<T, N-1>::print(t, os); // 递归,当N=2时,调用到特化模板
		os << std::get<N - 1>(t) << endl;
	}
};

template <typename T>
struct tuple_print<T, 1> // 特化模板,不能放在struct tuple_print之前,且只支持类模板
{
	static void print(const T& t, std::ostream& os)
	{
		os <<"1:" <<std::get<0>(t) << endl;
	}
}; 

template <typename... Args>
std::ostream& operator<<(std::ostream& os, const std::tuple<Args...>& t) // 覆盖cout的operator<<
{
	tuple_print<decltype(t), sizeof...(Args)>::print(t, os);
	return os;
}

int main(int, char *[])
{   
	std::tuple<int, string, double> tp(1, "abc", 5.1); // 等价于结构体A
	cout << std::get<0>(tp) << endl;
	cout << std::get<1>(tp).c_str() << endl;
	cout << std::get<2>(tp) << endl;
	 

	int x;
	string y;
	double z;
	std::tie(x, y, z) = tp;
	cout << x << endl;
	cout << y.c_str() << endl;
	cout << z << endl;

	auto tp2 = make_tuple(5, 6, 7);

	auto tt = std::tuple_cat(tp, tp2); // 拼接多个tuple
	
	cout << "------------------------------------" << endl;

	cout << tt << endl;
	system("pause");
	return 0;
}

 

 

 

 

 

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics