(c++笔记)第二十八节课:c++14扩展

16 篇文章 0 订阅

一 类型推导

1.1 意义

理解编译器推导规则有利于高效的使用c++
从明显或者冗余的类型拼写中解放出来,这样使得c++也更有适配性。

1.2 boost安装

sudo apt update
sudo apt-get install libboost-all-dev

1.3 反例

#include <iostream>
using namespace std;
​
int main(int argc, char const *argv[])
{
    const int num = 1;
    auto tmp = num;
    tmp++;
    return 0;
}

1.4 类型推导的使用场景

#include <iostream>
#include <boost/type_index.hpp>
#include <vector>
using boost::typeindex::type_id_with_cvr;
using namespace std;
//引用折叠
//T:int & func(T &&t) --->func(int & &&t)
//T:int && func(T &&t) --->func(int && &&t)
//折叠规则:有左即为左,全右则为右
//转发:完美转发
//std:forward<T>();
​
void test(int &num1)
{
    cout<<"int &num1"<<endl;
    cout<<"num1 = "<<num1<<endl;
}
/*void test(int &&num2)
{
    cout<<"int &&num2"<<endl;
    cout<<"num2 = "<<num2<<endl;
}*/
/*
万能引用(未定义引用):只能再函数模板中使用
作用:既能接收左值,也能接收右值
*/
//注意事项:
//1、模板参数必须紧跟&&符号
    //void func(vector<T> &&v)  //---》这个不是万能引用,因为vector和v之间有<T>
//2.const属性会剥夺万能引用的权限
    //void func(const T && Parm) //-->这个也不是万能引用,因为被const修饰
//3.万能引用不是一种新的数据类型,它只存在于函数模板中
template <typename T>
void print(T &&t)  //右值引用
//void print(T &num) //左值引用
{
    t++;
    cout<<t<<endl;
    test(std::forward<int &&>(t));
    //test(std::forward<int &>(t));
    test(t);
    cout<<"void print(T &&t)"<<endl;    
    cout<<"T type:"<<type_id_with_cvr<T>().pretty_name()<<endl;
    cout<<"t type:"<<type_id_with_cvr<decltype(t)>().pretty_name()<<endl; 
}
//按值传递
/*
int          --->推导规则:T:int t:int
const int    --->推导规则:T: int t:int(忽略const 属性)
int&         --->推导规则:T: int t:int(忽略&属性)
int*         --->推导规则:T: int* t:int*(不会忽略指针属性)
*/
#if 0
template<typename T>
void func(T t)
{
    cout<<"void func(T t)"<<endl;    
    cout<<"T type:"<<type_id_with_cvr<T>().pretty_name()<<endl;
    cout<<"t type:"<<type_id_with_cvr<decltype(t)>().pretty_name()<<endl;
}
​
//按指针传递
/*
int          --->推导规则:T:int       t:int*
const int    --->推导规则:T: const int t:const int*
*/
template<typename T>
void func(T *t)
{
    cout<<"void func(T *t)"<<endl;    
    cout<<"T type:"<<type_id_with_cvr<T>().pretty_name()<<endl;
    cout<<"t type:"<<type_id_with_cvr<decltype(t)>().pretty_name()<<endl;
}
#endif
//按引用传递 同引用
template<typename T>
void func(T &t)  //T:int t:int&
{
    cout<<"void func(T &t)"<<endl;    
    cout<<"T type:"<<type_id_with_cvr<T>().pretty_name()<<endl;
    cout<<"t type:"<<type_id_with_cvr<decltype(t)>().pretty_name()<<endl;
}
int main(int argc, char const *argv[])
{
#if 0
    int num = 5;
    print(5);
​
    int &num1 = num;
    int &&num2 = std::move(num);
    print(num1);
    print(num2);
​
​
//按值传递  推导规则
    int num =100;
    const int count = 0;
    int &num1 = num;
    func(num);
    func(count);
    func(num1);
    func(&num);
​
//按指针传递
    int num = 1;
    const int count = 0;
    func(&num);
    func(&count);
​
//按引用传递
    int num = 1;
    const int count = 0;
    func(num);
    func(count);
#endif
//万能引用--转发
    int num = 1;
    const int count = 0;
    print(6);
    return 0;
}

二 可调用对象

2.1 通过函数调用符操作的对象称之为可调用对象

2.2 可调用对象

普通函数
类成员函数/静态函数
类的成员函数指针
可转换为函数指针的类
#include <iostream>
using namespace std;
class Test
{
public:
    void print()  //成员函数
    {
        cout << "this is Test" << endl;
    }
};
void print(void *pa)
//void print()
{
    cout << "普通函数" << endl;
    delete pa;
}
//typedef void(*T)(void *);
using P_FUNC = void(*)(void *); 
class A
{
public:
    using P_FUNC = void(*)(void);
    static void print(void)
    {
        cout << "this is class A" << endl;
    }
    operator int()
    {
        return 1;
    }
    operator P_FUNC()  //操作隐式转换,使得对象A转化成P_FUNC类型对象,此时的返回值是P_FUNC,就是函数名
    {
        return print;
    }
};
int main(void)
{
    //print();
    Test t;
    t.print();
    P_FUNC p1 = print;
    //p1();
    void(Test::*p_func)(void) = &Test::print;   //p_func是一个成员函数指针
    (t.*p_func)();  //成员函数指针也可以作为可调用对象
    A a;
    a();
    cout << a << endl;
//-------
    //shared_ptr<A> p(new A(), print);
    shared_ptr<A> p(new A(), p1);//传回调函数
​
}

2.3 函数对象(仿函数)

重载函数调用运算符
函数对象和普通函数的区别
class B
{
public:
    void operator()(int a,int b)
    {
        cout << "hello class B" << endl;
    }
};
//内置的函数对象
    plus<int> p1;
    cout << p1(5, 6) << endl;
    minus<int> p2;
    cout << p2(7, 8) << endl;
    less<int> p3;
    cout << p3(7, 8) << endl;
    greater<int> p4;
    cout << p4(8, 9) << endl;

三 lambda 表达式

(1)本质

实际就是匿名函数,能够给捕获一定范围的变量
与普通函数不同,可以在函数的内部定义。

(2)格式

[捕获列表](参数列表)->返回值类型{语句块};
说明:
    1.返回值类型可以由编译器推导出来,可以省略不写
    2.参数可以有默认值
    3.c++14的lambda形参可以使用auto声明
    4.lambda的调用方法和普通函数一样
#include <iostream>
using namespace std;
​
int main(void)
{
    /*void print()
    {
        cout << "hello world" << endl;
    }*/
    //auto p = []() {};
    //auto p = [](auto num = 10)->int {};  //c++14后lambda可以推导形参
    auto p = [](void *)
    {
        cout << "hello world" << endl;
        return 'a';  //自动推导
    };
    //p();
//lambda的调用时机:1延时调用  2.定义并调用
    //shared_ptr<int> p2(new int(6), p);//1延时调用
    shared_ptr<int> p3(new int(5), [](void *p) {
        cout << "hello share ptr" << endl;
        delete p;
    });
    return 0;
}

(3) 捕获列表

#include <iostream>
#include <string>
using namespace std;
class A
{
public:
	A(int num, string s):m_num(num),m_s(s)
	{
		
	}
	void test()
	{
		int count = 10;
		//[&]或者[=]:默认捕获this指针
		auto p = [this]()  //只捕获this指针,可以修改,但是不能访问外部的变量
		{
			//cout << count << endl;
			cout << m_num << endl;
			cout << m_s << endl;
			cout << "lambda p" << endl;
		};
		p();
	}
public:
	int m_num;
	string m_s;
};
int main(void)
{
#if 0
	/*void print()
	{
		cout << "hello world" << endl;
	}*/
	//auto p = []() {};
	//auto p = [](auto num = 10)->int {};  //c++14后lambda可以推导形参
	auto p = [](void *)
	{
		cout << "hello world" << endl;
		return 'a';  //自动推导
	};
	//p();
//lambda的调用时机:1延时调用  2.定义并调用
	//shared_ptr<int> p2(new int(6), p);//1延时调用
	shared_ptr<int> p3(new int(5), [](void *p) {
		cout << "hello share ptr" << endl;
		delete p;
	});
#endif
//捕获列表:
	int num = 5;
	int count = 8;
	string s1 = "hello world";
	//auto p = []()  //不捕获外部的任何变量
	//auto p1 = [&]()  //按引用捕获外部所有的变量
	//auto p1=[=]()     //按等号捕获,只能使用外部所有的值,不能修改
	//auto p1 = [num,s1]()  //只能捕获num,count,只能使用,不能修改
	//auto p1 = [=,&num,&count] //其它变量都是按照等号捕获,num和count按照引用捕获
	auto p1 = [&,num,count]() //其它变量都是按照引用捕获,num,count按照值捕获
	{
		cout << "num1 = " << num << endl;
		//num = 100;
		s1 = "hello kitty";
		//cout << count << endl;
		cout << s1 << endl;
	};
	p1();
	cout << "num1 = " << num << endl;

	A a(1,"hello world");
	a.test();
	return 0;
}

(4)注意事项

1.引用悬挂
	按引用捕获会导致指向局部变量的引用,当lambda离开局部作用域时,会导致引用的那个变量被释放,lambda里面的那个引用会发生引用悬挂。
2. this陷阱
#include <iostream>
#include <vector>
#include <functional>
#include <algorithm>
#include <memory>
typedef std::function<void(void)> FP;
using namespace std;
class Point
{
public:
    Point(int x, int y) :m_x(x), m_y(y)
    {
​
    }
    void print()
    {
    //解决方法1:产生副本,捕获副本,x,y也存在失效的风险
        int x = m_x;
        int y = m_y;
        //this指针已经失效,空间释放,导致访问不到M_x,m_y
        cout << "x: " << m_x << " y: " << m_y << endl;
        //v1.push_back([=]() {cout << "x: " << m_x << " y: " << m_y << endl; });
        //v1.push_back([x,y]() {cout << "x: " << x << " y: " << y << endl; });
    //c++14:广义lambda捕获
        v1.push_back([a = m_x, b = m_y]() {cout << "x: " <<a << " y: " << b << endl; });
    }
    static void print_History()
    {
        for_each(v1.begin(), v1.end(), [](FP p) {
            if (p)
            {
                p();
            }
        });
    }
private:
    int m_x;
    int m_y;
    typedef function<void(void)> FP;
    static vector<FP> v1;
};
vector<FP> Point::v1 = vector<FP>();
int main(void)
{
    unique_ptr<Point> p;
    p.reset(new Point(1, 2));
    p->print();
    p.reset(new Point(2, 3));
    p->print();
    p.reset(new Point(3, 4));
    p->print();
    Point::print_History();
    return 0;
}

四 function包装器

4.1 本质

一个类模板,用于包装可调用对象,可以容纳除了类成员(函数)指针之外的所有可调用对象。

4.2 作用

可以用同一的方式来保存或者传递可调用对象。

4.3 意义

实现了一台消失机制,可以用统一的方式处理不同类型的可调用对象
function进一步深化以数据为中心的面向对象思想(函数也被对象化)。

4.4 案例

#include <iostream>
#include <functional>
using namespace std;
void print()
{
    cout << "hello world" << endl;
}
class Test
{
public:
    int operator()(int a, int b)
    {
        return a + b;
    }
};
class A
{
public:
    static int func(int a,int b)
    {
        cout << "this is class A" << endl;
        return a + b;
    }
};
int add(int a, int b)
{
    return a + b;
}
void test(int a, int b, int(*p)(int, int))   //仿函数无法调用
{
    cout << " int(*p)(int, int))" << endl;
    cout << p(a, b) << endl;
}
void test(int a, int b, function<int(int, int)> fp)  //适配性更好
{
    cout << "function<int(int, int)>" << endl;
    cout << fp(a, b) << endl;
}
int main(void)
{
#if 0
    function<void(void)> fp(print);
    fp();
    auto p = [](int a, int b)
    {
        return a + b;
    };
    function<int(int, int)> fp2(p);
    cout << fp2(1, 2) << endl;
​
​
    Test t;
    //function<int(int, int)> fp3(t);
    function<int(int, int)> fp3 = t;
​
    cout << fp3(1, 2) << endl;
    auto p2 = A::func;
    function<void(void)> fp4 = p2;
#endif
//function作为函数的参数
    test(1, 2, add);
    auto p = [](int a, int b)
    {
        return a + b;
    };
    test(6,7,p);
    Test t;
    test(9, 8, t);
    test(5, 6, A::func);
    return 0;
}

五 bind适配器

(1)本质

函数模板,返回值是一个仿函数,也是可调用对象
c++11合并之前的bind1和bind2

(2) 作用

将多元的可调用对象与其参数一定绑定成一个仿函数对象
将多元(n)的可调用对象转成一元或(n-1)元的可调用对象,即只绑定部分参数。

(3)bind可以绑定的对象

1> 普通函数

#include <iostream>
#include <functional>
using namespace std;
using namespace std::placeholders;  //使用占位符
int add(int a, int b)
{
    cout << "a = " << a << " ";
    cout << "b = " << b << endl;
    return a + b;
}
void test(function<int(void)> t)
{
    cout << t() << endl;
}
void test(function<int(int)> &t)
{
    cout << t(5) << endl;
}
int main(void)
{
    add(5, 6);
    //test(add);
    auto p = bind(add, 5, 6);  //将add转为int(void)
    test(p);
    cout << p() << endl;
​
    auto p1 = bind(add, placeholders::_1, 6);
    cout << p1(9) << endl;
​
    auto p2 = bind(add, 2, placeholders::_1);
    cout << p2(5) << endl;
​
    auto p3 = bind(add, std::placeholders::_2, std::placeholders::_1);
    p3(3, 4);
    return 0;
}

2> 函数对象

class Test  //函数对象
{
public:
    int operator()(int a, int b)
    {   
        cout << "a = " << a << " ";
        cout << "b = " << b << endl;
        return a + b;
    }
};
//绑定函数对象
    Test t;
    cout << t(5, 6) << endl;
    //test(t);
    auto p = bind(t, std::placeholders::_1, 6);
    test(p);
​
    less<int> le;
    cout << le(5, 6) << endl;
    auto p1 = bind(le, 5, std::placeholders::_1);
    cout << p1(6) << endl;

3> 类的成员函数(_1:必须是某个对象的地址)

#include <iostream>
#include<functional>
using namespace std;
using namespace std::placeholders;//使用占位符
int add(int a,int b)
{
    cout<<"a="<<a<<" ";
    cout<<"b="<<b<<endl;
    return a+b;
}

void text(function<int(void)> t)
{
    cout<<t()<<endl;
} 
void text1(const function<int(int)> &t)
{
    cout<<t(5)<<endl;
}

class Text//函数对象
{
    public:
        int operator()(int a,int b)
        {
            cout<<"a="<<a<<endl;
            cout<<"b="<<b<<endl;
            return a+b;
        }
        int add(int a,int b)
        {
            cout<<"a="<<a<<endl;
            cout<<"b="<<b<<endl;
            return a+b;
        }
};
int main(int argc, char const *argv[])
{
//适配成员函数
    //text(成员函数):error:function不能包装类的成员函数和成员函数指针,需要通过bind适配器可调用的对象
    //因为bind返回的是仿函数,它是一个可调用对象,所有可以用function包装器包装
    //适配成员函数时第一个参数是对象的地址
    Text t;
    auto p=bind(&Text::add,std::placeholders::_1,5,std::placeholders::_2);
    cout<<p(&t,3)<<endl;
    text(p);
    return 0;
}

4> 类的数据成员(_1:必须是某个对象的地址)

//适配成员属性
    Test t1;
    t1.m_a = 100;
    t1.m_count = 200;
    auto p1 = bind(&Test::m_a, t1);  //将t1和m_a进行绑定,t1按照值进行适配
    cout << p1() << endl;
    p1() = 1000;
    cout << t1.m_a << endl;

(4)使用案例

注意:bind预先绑定的参数需要传具体的变量或者值进去,对于预先绑定的参数是按值传递的。
stl中算法
#include <iostream>
#include <functional>
using namespace std;
using namespace std::placeholders;  //使用占位符
int add(int a, int b)
{
    cout << "a = " << a << " ";
    cout << "b = " << b << endl;
    return a + b;
}
/*void test(function<int(void)> &t)
{
    cout << t() << endl;
}
void test(const function<int(int)> &t)
{
    cout << t(5) << endl;
}*/
​
class Test  //函数对象
{
public:
    int operator()(int a, int b)
    {   
        cout << "a = " << a << " ";
        cout << "b = " << b << endl;
        return a + b;
    }
    int add(int a, int b)
    {
        cout << "a = " << a << " ";
        cout << "b = " << b << endl;
        return a + b;
    }
    int m_a;
    int m_count;
};
void test(function<int(Test *,int)> &t)
{
​
}
int main(void)
{
    
    //add(5, 6);
    test(add);
    //auto p = bind(add, 5, 6);  //将add转为int(void)
    //test(p);
    //cout << p() << endl;
​
    //auto p1 = bind(add, placeholders::_1, 6);
    //cout << p1(9) << endl;
​
    //auto p2 = bind(add, 2, placeholders::_1);
    //cout << p2(5) << endl;
​
    //auto p3 = bind(add, std::placeholders::_2, std::placeholders::_1);
    //p3(3, 4);
//绑定函数对象
    //Test t;
    //cout << t(5, 6) << endl;
    test(t);
    //auto p = bind(t, std::placeholders::_1, 6);
    //test(p);
​
    //less<int> le;
    //cout << le(5, 6) << endl;
    //auto p1 = bind(le, 5, std::placeholders::_1);
    //cout << p1(6) << endl;
//适配成员函数
    //test(成员函数)://error:function不能包装类的成员函数和成员函数指针,需要通过bind适配器成可调用的对象
    //因为bind返回的是仿函数,它是一个可调用对象,所以可以用function包装器包装
    //适配成员函数时第一个参数是对象的地址
    //Test t;
    //auto p = bind(&Test::add, std::placeholders::_1, 5,std::placeholders::_2);
    //cout << p(&t,3) << endl;
    test(p);
//适配成员属性
    Test t1;
    t1.m_a = 100;
    t1.m_count = 200;
    auto p1 = bind(&Test::m_a, t1);  //将t1和m_a进行绑定,t1按照值进行适配
    cout << p1() << endl;
    p1() = 1000;
    cout << t1.m_a << endl;
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值