本文内容:auto,function,bind,decltype,lambda,initializer_list
文章目录
- [第15章 C++重要知识点](#第15章 C++重要知识点)
-
- [15.5 理解auto类型推断,auto应用场合](#15.5 理解auto类型推断,auto应用场合)
-
- [15.5.1 auto 类型常规推断](#15.5.1 auto 类型常规推断)
-
- [15.5.1.1 传值方式(非指针非引用)](#15.5.1.1 传值方式(非指针非引用))
- [15.5.1.2 传递指针和引用 但不是万能引用](#15.5.1.2 传递指针和引用 但不是万能引用)
- [15.5.1.3 auto 与 万能引用](#15.5.1.3 auto 与 万能引用)
- [15.5.2 auto 类型针对数组和函数的推断](#15.5.2 auto 类型针对数组和函数的推断)
- [15.5.3 auto std::initializer_list 特殊推断](#15.5.3 auto std::initializer_list 特殊推断)
- [15.5.4 auto 不适用场景](#15.5.4 auto 不适用场景)
- [15.5.5 使用场合](#15.5.5 使用场合)
-
- [5.5.6.1 在stl中使用](#5.5.6.1 在stl中使用)
- [5.5.6.2 在模板的返回值中使用](#5.5.6.2 在模板的返回值中使用)
- [15.6 详解decltype含义,decltype主要用途](#15.6 详解decltype含义,decltype主要用途)
-
- [15.6.1 decltype 作用和举例](#15.6.1 decltype 作用和举例)
-
- [15.6.1.1 decltype 后面圆括号是变量](#15.6.1.1 decltype 后面圆括号是变量)
- [15.6.1.2 decltype 后面的括号中是非变量(表达式)](#15.6.1.2 decltype 后面的括号中是非变量(表达式))
- [15.6.1.3 decltype后圆括号中是函数](#15.6.1.3 decltype后圆括号中是函数)
- [15.6.2 decltype 主要用途,C++11方式](#15.6.2 decltype 主要用途,C++11方式)
-
- [15.6.1.1 用途1:应付可变类型](#15.6.1.1 用途1:应付可变类型)
- [15.6.1.2 decltype 与 临时对象的使用](#15.6.1.2 decltype 与 临时对象的使用)
- [15.6.1.3 通过变量表达式抽取变量类型](#15.6.1.3 通过变量表达式抽取变量类型)
- [15.6.1.4 decltype 与 auto构成后置语法](#15.6.1.4 decltype 与 auto构成后置语法)
- [15.6.1.5 decltype(auto) 方式](#15.6.1.5 decltype(auto) 方式)
-
- [1 decltype(auto) 在函数中使用](#1 decltype(auto) 在函数中使用)
- [2 decltype(auto) 在变量中使用](#2 decltype(auto) 在变量中使用)
- 15.7可调用对象、std::function、std::bind
-
- [15.7.1 可调用对象](#15.7.1 可调用对象)
-
- [15.7.1.1 函数指针](#15.7.1.1 函数指针)
- [15.7.1.2 具有operator() 成员函数的类对象(仿函数,函数对象)](#15.7.1.2 具有operator() 成员函数的类对象(仿函数,函数对象))
- [15.7.1.3 可被转换为函数指针的函数对象](#15.7.1.3 可被转换为函数指针的函数对象)
- [15.7.1.4 类成员函数指针](#15.7.1.4 类成员函数指针)
- [15.7.1.5 总结](#15.7.1.5 总结)
- [15.7.2 std::function 可调用对象包装器](#15.7.2 std::function 可调用对象包装器)
-
- [15.7.2.1 function绑定普通函数](#15.7.2.1 function绑定普通函数)
- [15.7.2.2 function绑定类的静态成员函数](#15.7.2.2 function绑定类的静态成员函数)
- [15.7.2.3 function绑定仿函数](#15.7.2.3 function绑定仿函数)
- [15.7.2.4 使用示例](#15.7.2.4 使用示例)
-
- 范例1:function作为函数参数
- [范例2 :function作为函数参数 实现回调功能](#范例2 :function作为函数参数 实现回调功能)
- [15.7.3 std::bind 绑定器](#15.7.3 std::bind 绑定器)
-
- [15.7.3.1 bind 占位符](#15.7.3.1 bind 占位符)
- [15.7.3.2 bind 值传递](#15.7.3.2 bind 值传递)
- [15.7.3.3 绑定成员函数](#15.7.3.3 绑定成员函数)
- [15.7.3.4 绑定类的对象](#15.7.3.4 绑定类的对象)
- [15.7.4 function 和 bind 结合使用](#15.7.4 function 和 bind 结合使用)
-
- [示例1: function + bind 结合使用](#示例1: function + bind 结合使用)
- [示例2: 使用function+map实现 switch功能](#示例2: 使用function+map实现 switch功能)
- [示例3: function的实现原理](#示例3: function的实现原理)
- [示例4: bind和function实现线程池](#示例4: bind和function实现线程池)
- [15.7.5 总结](#15.7.5 总结)
第15章 C++重要知识点
15.5 理解auto类型推断,auto应用场合
auto 和模板推倒有点相似,就是将T 自动推断为auto。
auto的特点如下:
特点1:auto的自动类型推断发生在编译期间;
特点2: auto定义变量必须立即初始化,这样编译器才能推断出它的实际类型。编译的时候才能确定auto的类型和整个变量的类型,然后在编译期间就可以用真正的类型替换掉auto这个类型占位符。
特点3:auto的使用比较灵活,可以和指针、引用、const等限定符结合使用。
15.5.1 auto 类型常规推断
auto C++11的产物,auto用于变量的自动类型推断,在声明变量时候根据变量初始化类型自动为此变量选择匹配的类型,而不需要我们显示制定。
15.5.1.1 传值方式(非指针非引用)
auto后边直接跟上变量名,叫传值方式。
cpp
void test()
{
auto x = 25;
const auto x2 = x; // const int, auto 是 int
const auto& xy = x; // xy = const int & ,auto 还是 int类型。
auto xy2 = xy; // xy2 = int , 因为在传值时,引用和const 会被抛弃,xy本来是 const int & 类型,但是const 和 &都被抛弃了。
}
验证:
cpp
void test()
{
int x = 25;
const int x2 = x;
const int & xy = x;
int xy2 = xy; // xy本来是 const int &
}
15.5.1.2 传递指针和引用 但不是万能引用
auto 与指针和引用的结合。
实验1:向模板中传递 左值。
cpp
template<typename T>
void tf(const T & tmprv)
{
}
void test()
{
auto x = 25; // auto = int ; int x = 25;
const auto x2 = x; // auto = int, const int x2
const auto& xy = x; // xy = const int &
auto xy2 = xy; // xy2 = int , 因为在传值时,引用& 和const 会被抛弃; int xy2 = xy;
tf(x);
}
推断:
cpp
template<typename T>
void tf(const T & tmprv)
{
}
/* First instantiated from: insights.cpp:18 */
#ifdef INSIGHTS_USE_TEMPLATE
template<>
void tf<int>(const int & tmprv)
{
}
#endif
void test()
{
int x = 25;
const int x2 = x;
const int & xy = x;
int xy2 = xy;
tf(x);
}
分析:向模板中传递左值,const T & tmprv 被推断出来的仍然是左值:const int & tmprv;
2 :向模板传递const int 类型,得到的结果就是const auto & x2 ,
cpp
template <typename T>
void tf(const T& tmprv)
{
}
void test()
{
auto x = 25;
const auto x2 = x; //auto是int,整理 const int x2
const auto& xy = x; // xy = const int &
auto xy2 = xy; // xy2 = int , 因为在传值时,引用和const 会被抛弃;
//tf(x);
tf(x2);
//tf(xy);
}
推导:
cpp
template<typename T>
void tf(const T & tmprv)
{
}
/* First instantiated from: insights.cpp:19 */
#ifdef INSIGHTS_USE_TEMPLATE
template<>
void tf<int>(const int & tmprv)
{
}
#endif
void test()
{
int x = 25;
const int x2 = x;
const int & xy = x;
int xy2 = xy;
tf(x2);
}
分析,向模板传递的是 const int & , T 被推断为 int类型。
3:传递const + 引用, const int &
cpp
template <typename T>
void tf(const T& tmprv)
{
}
void test()
{
auto x = 25;
const auto x2 = x; // const int
const auto& xy = x; // xy = const int &
auto xy2 = xy; // xy2 = int , 因为在传值时,引用和const 会被抛弃;
//tf(x);
//tf(x2);
tf(xy);
}
推导:
cpp
template<typename T>
void tf(const T & tmprv)
{
}
/* First instantiated from: insights.cpp:20 */
#ifdef INSIGHTS_USE_TEMPLATE
template<>
void tf<int>(const int & tmprv)
{
}
#endif
void test()
{
int x = 25;
const int x2 = x;
const int & xy = x;
int xy2 = xy;
tf(xy);
}
分析:推倒出来的结果也是 const T & tmprv 被推断出const int & tmprv
4:auto推断指针
cpp
void test()
{
auto x = 25;
const auto x2 = x; // const int
const auto& xy = x; // xy = const int &
auto xy2 = xy; // xy2 = int , 因为在传值时,引用和const 会被抛弃;
auto xy3 = xy;
auto y = new auto(100);// auto y 被推断出 int * , int * y = new int{100};
const auto* xp = &x; // auto被推倒为 int ,x是引用 &
auto* xp2 = &x;
auto xp3 = &x;
}
验证:
cpp
void test()
{
int x = 25; // auto x , 推断为 int x ,
const int x2 = x; // x 是 int ,推断出来的也是:auto是 int
const int & xy = x;// x时候 const int ,xy 是 int ,x中的 const 背会丢弃掉。
int xy2 = xy;
int xy3 = xy; // 传递 const int & , xy2 仍然是 int类型,const 和 & 被丢弃。
int * y = new int{100};
const int * xp = &x;
int * xp2 = &x;
int * xp3 = &x;
}
15.5.1.3 auto 与 万能引用
auto 与 && 完成引用时,也会发生引用折叠现象。
cpp
void test2()
{
auto&& wn = 12;
auto&& wn3 = wn;// && + & = & 所以 wn3是左值引用。
}
推导:
cpp
void test2()
{
int && wn = 12;
int & wn3 = wn;
}
15.5.2 auto 类型针对数组和函数的推断
C++中数组引用的写法,mystr2 是对 mystr 的引用。
cpp
const char mystr[] = "I Love China";
const char(&mystr2)[13] = mystr; // 数组的引用
cout << "mystr2[0] " << mystr2[0]; // 输出 I
void test()
{
const char mystr[] = "I Love China";
const char(&mystr2)[13] = mystr; // 数组的引用
cout << "mystr2[0] " << mystr2[0]; // 输出 I
int a[2] = { 1,2 };
auto aauto = a;
auto& att = a;
}
auto 自动类型推倒结果:
cpp
void test()
{
const char mystr[13] = "I Love China";
const char (&mystr2)[13] = mystr;
std::operator<<(std::operator<<(std::cout, "mystr2[0] "), mystr2[0]);
int a[2] = {1, 2};
int * aauto = a;
int (&att)[2] = a;
}
auto 与 函数的结合:
cpp
void test()
{
const char mystr[] = "I Love China";
const char(&mystr2)[13] = mystr; // 数组的引用
cout << "mystr2[0] " << mystr2[0]; // 输出 I
int a[2] = { 1,2 };
auto aauto = a;
auto& att = a;
// 函数的 auto
auto pf = myfunc;
pf(2); // 方式1:函数的指针方式
auto& pf2 = myfunc; // 方式2:引用方式
pf2(2);
using FuncPtr_21 = void (*)(int); // 方式1的推倒:定义一个函数指针类型,指向了 myfunc 函数,这个指针也可以指向不同的类型;
FuncPtr_21 pf = myfunc;
pf(2);
void(&pf2)(int) = myfunc; // 方式2的推倒:定义一个函数引用,该是myfunc的别名,只能指向myfunc函数,不能再指向其他函数了。
pf2(2);
}
15.5.3 auto std::initializer_list 特殊推断
当auto 遇到={}时候,推导就是initialist_list ,这是一种特殊的推导
cpp
auto x5 = { 1 }; // x5= class std::initializer_list<int>
void test()
{
// C++98 写法
int x = 10;
int x2(20);
// C++11 写法
int a = { 30 };
int b{ 20 };
// 改为 auto
auto x1 = 10;
auto x21(20);
auto a1 = { 30 };
auto b1{ 30 };
}
自动推导为:
cpp
void test()
{
int x = 10;
int x2 = 20;
int a = {30};
int b = {20};
int x1 = 10;
int x21 = 20;
std::initializer_list<int> a1 = std::initializer_list<int>{30};
int b1 = {30};
}
当auto 遇到 ={} 这种语法时,会将推断为std::initializer_list数组,然后遇到{}中的类型后,再次推断std::initializer_list 数组的类型,比如{30} 推断数组类型为整型。
记忆方式:={} 这种是隐士类型转换,auto 遇到这种隐士类型转换就推断为了init数组。
15.5.4 auto 不适用场景
1 auto 不能用与函数参数,比如 void func(auto x, int y)

2 auto 在类中定义成员变量
cpp
class CT
{
public:
auto m_i = 12; // 语法不支持
static const auto m_si = 12; // 可以
};
15.5.5 使用场合
5.5.6.1 在stl中使用
auto在迭代器中使用。
cpp
std::map<string, int> mymap;
mymap.insert({ "aa",1 });
mymap.insert({ "bb",2 });
mymap.insert({ "cc",3 });
for (auto iter = mymap.begin(); iter != mymap.end(); ++iter)
{
cout << iter->first << " " << iter->second << endl;
}
5.5.6.2 在模板的返回值中使用
根据T推导出 类中成员函数的返回值。
cpp
class A
{
public:
static int testr()
{
return 0;
}
};
class B
{
public:
static double testr()
{
return 10.5;
}
};
template <typename T>
auto ftestclass()
{
auto value = T::testr();
return value;
}
void test3()
{
ftestclass<A>();
}
推导:
cpp
class A
{
public:
static inline int testr()
{
return 0;
}
};
class B
{
public:
static inline double testr()
{
return 10.5;
}
};
template<typename T>
auto ftestclass()
{
auto value = T::testr();
return value;
}
#ifdef INSIGHTS_USE_TEMPLATE
template<>
int ftestclass<A>()
{
int value = A::testr();
return value;
}
#endif
void test3()
{
ftestclass<A>();
}
15.6 详解decltype含义,decltype主要用途
15.6.1 decltype 作用和举例
decltype 对于一个给定的变量名和表达式,能够推倒出名字或者表达式的类型。
decltype和auto有类似之处,两者都是用来推断类型的。
decltype有如下特点:
decltype的自动类型推断也发生在编译期,这一点和auto一样。
decltype不会真正计算表达式的值。
const限定符、引用属性等有可能会被auto抛弃,但decltype 一般不会抛弃任何东西。
15.6.1.1 decltype 后面圆括号是变量
cpp
const int i = 0;
const int &iy = i;
auto j1 = 1; // 传递方式推断,引用& 和const 属性都会被抛弃。
decltype(i) j2 = 15; // j2 = const int , 如果decltype 中是个变量,则变量的const 属性会返回。
decltype(iy) j3 = j2; // j3 = const int & ,
测试代码:
cpp
class CT
{
public:
int i;
int j;
};
void test()
{
decltype(CT::i) a; // int
CT temp; // CT
decltype(temp) temp2; // CT
decltype(temp.i) mv = 2; // int
}
15.6.1.2 decltype 后面的括号中是非变量(表达式)
测试:
cpp
void test2()
{
decltype(8) kkk = 2;
int i = 0;
int* pi = &i;
int& iy = i;
decltype(iy + 1) j; // iy + 1 得到的是一个整型,所以推断出int
decltype(pi) k;// k 是 int *,因为 pi类型是int *;
*pi = 4;
decltype(i) k2;
decltype(*pi) k3 = i;//
}
编译器推导
cpp
void test2()
{
int kkk = 2;
int i = 0;
int * pi = &i;
int & iy = i;
int j;
int * k;
*pi = 4;
int k2;
int &k3 = i;
}
decltype(*pi) 为什么推断为 int & ?
1 因为指针指向了对象,且这个对象可以被赋值,所以是左值。
2 如果表达式结果作为赋值语句左侧的值,那么decltype 返回的就是一个引用。
另一种得到左值的方式:双层括号。
cpp
// 另一种得到左值的方式
decltype((i)) ii = i; // int & ii = i;
decltype((变量)) 得到的结果永远是引用。
15.6.1.3 decltype后圆括号中是函数
cpp
int testf()
{
return 1;
}
decltype(testf()) tmpv = 14; // tmpv 类型是一个函数返回值类型 int, 编译器没有调用该testf()函数,只使用了函数testf()返回值类型
decltype(testf) tmpv2; // tmpv2 = int(void ) ,有返回值 有函数类型,表示一个可调用对象。
function <decltype(testf)> ftmp = testf; // 声明了一个function 类型,用来代表一个可调用对象。
cpp
int testf()
{
return 1;
}
const int&& myfunc()
{
return 0;
}
void test3()
{
decltype(testf()) tmpv = 14; // tmpv 类型是一个函数返回值类型 int, 编译器没有调用该testf()函数,只使用了函数testf()返回值类型
decltype(testf) tmpv2; // tmpv2 = int(void ) ,有返回值 有函数类型,表示一个可调用对象。
//std::function<decltype(testf)> ftmp ;//= testf; // 声明了一个function 类型,用来代表一个可调用对象。
decltype(myfunc()) mm = 1;
}
const int && myfunc()
{
return 0;
}
void test3()
{
int tmpv = 14;
int tmpv2();
const int && mm = 1;
}
15.6.2 decltype 主要用途,C++11方式
15.6.1.1 用途1:应付可变类型
问题引入:decltype 应用于模板编程中。
cpp
template <typename T>
class TMP
{
public:
typename T::iterator iter; // std::vector<int>::iterator iter =
// T 用于告诉编译器这是一个类型
void getbegin(T& tmpc)
{
iter = tmpc.begin();
}
};
void test()
{
using conttype = std::vector<int>;
conttype myarr = { 10,20,30 };
TMP<conttype> ct;
ct.getbegin(myarr);
}
编译器推导后:
cpp
/* First instantiated from: insights.cpp:22 */
#ifdef INSIGHTS_USE_TEMPLATE
template<>
class TMP<std::vector<int, std::allocator<int> > >
{
public:
__gnu_cxx::__normal_iterator<int *, std::vector<int, std::allocator<int> > > iter;
inline void getbegin(std::vector<int, std::allocator<int> > & tmpc)
{
this->iter.operator=(tmpc.begin());
}
// inline constexpr TMP() noexcept = default;
};
#endif
void test()
{
using conttype = std::vector<int>;
std::vector<int, std::allocator<int> > myarr = std::vector<int, std::allocator<int> >{std::initializer_list<int>{10, 20, 30}, std::allocator<int>()};
TMP<std::vector<int, std::allocator<int> > > ct = TMP<std::vector<int, std::allocator<int> > >();
ct.getbegin(myarr);
}
std::allocator 是分配器。
现在将 using conttype = const std::vector; 声明为常量,修改后代码报错,使用C++98方式解决:类模板的偏特化。但是这种处理方式不好,偏特化只是为了解决类型问题,在偏特化出来的类模板中,其他成员函数都要重写一遍。
cpp
template <typename T>
class TMP
{
public:
typename T::iterator iter; // std::vector<int>::iterator iter =
// T 用于告诉编译器这是一个类型
void getbegin(T& tmpc)
{
iter = tmpc.begin();
}
};
// 加上这个偏特化版本后可解决 const std::vector<int>; 问题
template<typename T>
class TMP<const T> // 参数范围上的偏特化
{
typename T::iterator iter; // std::vector<int>::iterator iter =
// T 用于告诉编译器这是一个类型
void getbegin(T& tmpc)
{
iter = tmpc.begin();
}
};
void test()
{
using conttype = const std::vector<int>; // 加上const 之后报错,因为上面的 T::iterator iter; 而不是 T::const_iterator iter
//using conttype = std::vector<int>;
conttype myarr = { 10,20,30 };
TMP<conttype> ct;
ct.getbegin(myarr);
}
实现 decltype 解决:不需要偏特化了,传递什么类型,就能识别什么类型。
cpp
// 使用 decltype 改进
template <typename T>
class TMP
{
public:
decltype(T().begin()) iter;
// T 用于告诉编译器这是一个类型
void getbegin(T& tmpc)
{
iter = tmpc.begin();
}
};
void test()
{
using conttype = const std::vector<int>; // 加上const 之后报错,因为上面的 T::iterator iter; 而不是 T::const_iterator iter
//using conttype = std::vector<int>;
conttype myarr = { 10,20,30 };
TMP<conttype> ct;
ct.getbegin(myarr);
}
}
15.6.1.2 decltype 与 临时对象的使用
decltype 并没有真正构造对象,也没有真正调用A的成员函数func,而是直接获取了func的返回值。
cpp
class A
{
public:
A()
{
cout << "A()" << endl;
}
~A()
{
cout << "~A()" << endl;
}
int func() const
{
cout << "func()" << endl;
return 0;
}
};
void test()
{
//A().func(); // 临时对象:A() func() ~A()
//(const A()).func(); // 临时对象:A() func() ~A()
decltype(A().func()) x = 0; // 直接调用返回值。
}
15.6.1.3 通过变量表达式抽取变量类型
decltype 与 typedef 使用。
cpp
void test()
{
vector<int> ac = { 1,2,3 };
vector<int>::size_type mysize = ac.size();
cout << mysize << endl;
decltype(ac)::size_type mysize2 = ac.size();
cout << mysize2 << endl;
//
typedef decltype(sizeof(double)) mysize3; // mysize3 = size_t
mysize3 mysize4 = 12;
}
15.6.1.4 decltype 与 auto构成后置语法
回顾函数后置返回值。表示函数返回类型放到参数列表之后,通过 -> 开始。
cpp
auto func(int a) -> int
{
return a;
}
int &tf(int &i)
{
return i;
}
double tf(double &d)
{
return d;
}
// 调用整形
template<typename T>
auto FuncTmp(T &tv)->decltype(tf(tv)) // 返回类型后置 推断的含义
{
return tf(tv);
}
void test()
{
int i3 = 1;
FuncTmp(i3);
}
使用 auto 无法完成 decltype功能。因为直接使用auto(a+b)时,a b 还没有被定义。
auto func2(int a, int b) -> decltype(a + b)
{
return a + b;
}
//auto (a + b) func3(int a, int b) // 这种写法不被允许,引用 a+b还没有定义
//{
// return a + b;
//}
15.6.1.5 decltype(auto) 方式
如何理解 这种方式呢?auto 理解为自动类型推倒,decltype 理解为 推倒过程中采用 decltype 方式不丢失 const 和 & 来推倒。
1 decltype(auto) 在函数中使用
cpp
template <typename T>
T& mydouble(T& v1)
{
v1 = v1 * 2;
return v1;
}
// 使用 auto 修改上面函数的返回值类型
template <typename T>
auto mydouble2(T& vl) -> decltype(vl)
{
vl = vl * 2;
return vl;
}
template <typename T>
auto mydouble3(T& vl) // 直接用auto 返回,会丢弃const 和 &,所以返回的是右值
{
vl = vl * 2;
return vl;
}
template <typename T>
decltype(auto) mydouble4(T& vl) // C++14 引入语法既包含自动推倒,又不丢失 const 和 & 属性
{
vl = vl * 2;
return vl;
}
void test3()
{
int a = 100;
mydouble2(a) = 22;
mydouble3(a) = 22; // 这里报错:因为上边返回的不是左值,而是右值
}
2 decltype(auto) 在变量中使用
根据后边的值推倒前边。
cpp
void test()
{
int a = 100;
const int& y = 1;
auto z = y; // z = int
decltype(auto) z2 = y; // z2 = const int &
}
15.7可调用对象、std::function、std::bind
15.7.1 可调用对象
可调用对象主要有两种,函数指针和重载了() 运算符的类对象。
15.7.1.1 函数指针
cpp
static void myfunc(int v)
{}
void(*pf)(int) = myfunc;
pf(15); // 函数调用
15.7.1.2 具有operator() 成员函数的类对象(仿函数,函数对象)
C++ 仿函数通过类中重载()运算符,又称为函数对象(仿函数 something that performs a function );
调用方式:对象+(),比如 对象() 。
cpp
class TC
{
public:
void operator()(int tv)
{
cout << "仿函数" << tv << endl;
}
void pffunc(int tv)
{
cout << "TC pffunc 执行了" << endl;
}
static int stcfunc(int tv)
{
cout << "类的静态函数执行" << endl;
return tv;
}
};
void test()
{
TC tc;
tc(20); // 调用 operator() , 这也是可调用对象,类似于函数调用。
}
15.7.1.3 可被转换为函数指针的函数对象
类型转换运算符在类中,只能转换static 类型的函数,因为非static 成员函数,有一个默认的this指针。
可被转换为函数指针的类对象也可以叫作仿函数或函数对象。在MyProject.cpp前面增加如下TC2类定义:
cpp
class TC2
{
public:
using tfpoint = void(*)(int);
static void mysfunc(int tv)
{
cout << "TC2::mysfunc()静态成员函数执行了,tv=" << tv << endl;
}
operator tfpoint()
{
return mysfunc;
}
};
void test()
{
TC2 tc2;
tc2(100); // 先调用 tfpoint() 转换函数,然后调用 mysfunc() 静态成员函数
}
类型转换函数的用法:
下面例子,类型转换函数可以将一个类型转为double类型。
cpp
class Fraction
{
public:
// 放置隐士转换
explicit Fraction(int num, int den = 1) : m_numerator(num), m_denominator(den)
{
}
operator double() const
{
return (double)m_numerator / m_denominator;
}
private:
int m_numerator; // 分子
int m_denominator; // 分母
};
void test3()
{
Fraction f(3, 5);
double d = f; // 调用 operator double() 函数
cout << d << endl;
}
15.7.1.4 类成员函数指针
cpp
class TC
{
public:
void operator()(int tv)
{
cout << "仿函数" << tv << endl;
}
void pffunc(int tv)
{
cout << "TC pffunc 执行了" << endl;
}
static int stcfunc(int tv)
{
cout << "类的静态函数执行" << endl;
return tv;
}
};
void test()
{
TC tc2;
void (TC:: * myfpoint)(int) = &TC::pffunc;
(tc2.*myfpoint)(2); // 也是一个可调用对象
void (TC:: * myfunc)(int) = &TC::operator();
(tc2.*myfunc)(2);
}
15.7.1.5 总结
其实,可调用对象首先被看作一个对象,程序员可以对其使用函数调用运算符"()",那就可以称其为"可调用的"。换句话说,如果对象a是一个可调用对象,那么就可以编写诸如
a(参数1,参数2,)这样的代码。
如果找通用性,这几种可调用对象的调用形式都比较统一,除了类成员指针写法比较特殊以外,其他几种可调用对象的调用形式都是"名字(参数列表)"。但是,它们的定义方法是五花八门,怎样定义的都有。
那么,有没有什么方法能够把这些可调用对象的调用形式统一一下呢?有,那就是使用std::function把这些可调用对象包装起来。
b 我们可以对其使用() 调用运算符,如果a是可调用对象,那么我们就可以编写a代码。
15.7.2 std::function 可调用对象包装器
如何能把不同的可调用对象的形式统一起来,方便调用。(统一处理函数对象)
std::function 可调用对象包装器,是一个类模板 C++11引入 ,用来包装可调用对象,对外调用方式统一。
std::function 类模板特点,能够通过给它指定模板参数,它就能够用统一的方式调用。
15.7.2.1 function绑定普通函数
function<void(int)> 的 <> 中是 返回值类型+函数参数。
cpp
static void myfunc(int v)
{}
std::function<void(int)> f1 = myfunc;
f1(100);
15.7.2.2 function绑定类的静态成员函数
类的静态成员函数。
cpp
class TC
{
public:
void operator()(int tv)
{
cout << "仿函数" << tv << endl;
}
void pffunc(int tv)
{
cout << "TC pffunc 执行了" << endl;
}
static int stcfunc(int tv)
{
cout << "类的静态函数执行" << endl;
return tv;
}
};
void test()
{
std::function<int(int)> f = TC::stcfunc;
f(2);
}
15.7.2.3 function绑定仿函数
function 绑定仿函数时只需要将 重载了 operator类的对象赋值给 function.
cpp
TC tc3;
std::function<void(int)> f3 = tc3;
f3(2);
15.7.2.4 使用示例
范例1:function作为函数参数
函数对象function<> 作为函数参数传递,并将重载了operator的类对象绑定到 function上。
cpp
class CB
{
public:
// 初始化一个无参函数对象
CB(const std::function<void()>& f) : fcallback(f)
{
int i;
i = 1;
}
void runcallback(void)
{
fcallback();
}
private:
std::function<void()> fcallback; // 可调用对象包装器
};
class CT
{
public:
void operator()(void)
{
cout << "TT operator() 执行了" << endl;
}
};
void test()
{
CT ct; // 可调用对象
CB cb(ct); // cb 需要可调用对象左参数来构造,ct因为有 operator() 所以可以转换 std::function<void()&>对象 ;
cb.runcallback();
}
范例2 :function作为函数参数 实现回调功能
cpp
// 定义回调函数
void mycallback(int cs, const std::function<void(int)> &f)
{
f(cs);
}
// 定义要运行回调函数
void runfunc(int x)
{
cout << x << endl;
}
int test()
{
// 循环调用回调函数
for (int i = 0; i < 10; ++i)
{
mycallback(i, runfunc);
}
}
15.7.3 std::bind 绑定器
C++11 引入的bind绑定器。
std::bind 能够将对象以及相关参数绑定在一起,绑定完之后直接调用,也可以用std::function 进行保存;
cpp
格式
/*
std::bind(待绑定的函数对象/函数指针/成员函数指针, 参数绑定值1,参数绑定值2,... 参数绑定值n)
总结:
1 将可调用对象和参数绑定在一起,构造一个仿函数,所以可以直接调用。
2 如果函数有多个参数,可以绑定一部分参数,其他参数在调用的时候制定。
*/
void myfunc1(int x, int y, int z)
{
cout << x << " " << y << " " << z << " " << endl;
}
void myfun2(int &a, int &b)
{
a++;
b++;
}
void test()
{
auto bf1 = std::bind(myfunc1, 1, 2, 3); // auto 不关心bind 返回类型,其他bind返回一个仿函数对象,
bf1(); // 执行仿函数
auto bf2 = std::bind(myfunc1, placeholders::_1, placeholders::_2, 30); // 只是占位符,调用bf2时候,再制定第二个参数。
bf2(1, 2);
}
15.7.3.1 bind 占位符
系统定义了20个占位符,足够我们使用了。
cpp
auto bf2 = std::bind(myfunc1, placeholders::_1, placeholders::_2, 30); // 只是占位符,调用bf2时候,再制定第二个参数。
bf2(1,2);
15.7.3.2 bind 值传递
对于placeholders绑定的参数,是通过值传递的;直接占位是引用传递。
cpp
int a = 2;
int b = 3;
auto bf3 = std::bind(myfun2,placeholders::_1,a);
cout << a << endl; // 对于绑定的参数,是通过值传递的;
cout << b << endl; // 对于bind是通过
15.7.3.3 绑定成员函数
如果绑定成员函数,第一个参数绑定类的成员函数,第二个参数绑定对象的引用;否则会出现调用成员函数。
cpp
class CT
{
public:
void myfuncf(int a, int b)
{
ma = a;
}
int ma;
CT()
{
cout << "构造函数" << endl;
}
CT(const CT & ct)
{
cout << "拷贝构造函数" << endl;
}
void operator()(void)
{
cout << "operator() 执行" << endl;
}
};
void test()
{
CT ct;
// auto bf5 = std::bind(&CT::myfuncf, ct, std::placeholders::_1, std::placeholders::_2);
auto bf5 = std::bind(&CT::myfuncf, &ct, std::placeholders::_1, std::placeholders::_2);
bf5(10, 20); // 上行第二个参数ct,会导致调用CT的拷贝构造函数来生成一个CT 临时对象,作为std::bind的返回值(bind);
// 后续 myfunc 成员函数调用,修改的是临时对象的ma值;并不会影响真实的ct对象的值。
}
15.7.3.4 绑定类的对象
cpp
auto rt = std::bind(CT());
rt();
15.7.4 function 和 bind 结合使用
示例1: function + bind 结合使用
cpp
// 示例函数:普通函数
int add(int a, int b)
{
return a + b;
}
// 示例类
class Calculator
{
public:
int multiply(int a, int b)
{
return a * b;
}
};
int main() {
// 使用 std::function 封装普通函数
std::function<int(int, int)> addFunction = add;
std::cout << "addFunction(3, 4): " << addFunction(3, 4) << std::endl; // addFunction(3, 4): 7
// 使用 std::bind 绑定参数
std::function<int(int)> addThree = std::bind(add, 3, std::placeholders::_1);
std::cout << "addThree(5): " << addThree(5) << std::endl; // addThree(5): 8
// 使用 std::function 封装类的成员函数
Calculator calc;
std::function<int(Calculator&, int, int)> multiplyFunction = &Calculator::multiply;
std::cout << "multiplyFunction(calc, 2, 6): " << multiplyFunction(calc, 2, 6) << std::endl;// multiplyFunction(calc, 2, 6): 12
// 使用 std::bind 绑定类成员函数和对象
std::function<int(int)> multiplyByTwo = std::bind(&Calculator::multiply, &calc, std::placeholders::_1, 2);
std::cout << "multiplyByTwo(7): " << multiplyByTwo(7) << std::endl;// multiplyByTwo(7): 14
return 0;
}
示例2: 使用function+map实现 switch功能
map<int, function<void()>> actionMap;
cpp
void doShowAllBooks() { cout << "查看所有书籍信息" << endl; }
void doBorrow() { cout << "借书" << endl; }
void doBack() { cout << "还书" << endl; }
void doQueryBooks() { cout << "查询书籍" << endl; }
void doLoginOut() { cout << "注销" << endl; }
int main()
{
int choice = 0;
map<int, function<void()>> actionMap;
actionMap.insert({1,doShowAllBooks});
actionMap.insert({ 2, doBorrow });
actionMap.insert({ 3, doBack });
actionMap.insert({ 4, doQueryBooks });
actionMap.insert({ 5, doLoginOut });
while (1)
{
cout << "-------------------" << endl;
cout << "1.查看所有书籍信息" << endl;
cout << "2.借书" << endl;
cout << "3.还书" << endl;
cout << "4.查询书籍" << endl;
cout << "5.注销" << endl;
cout << "-------------------" << endl;
cout << "请选择";
cin >> choice;
if (choice == 0)
{
break;
}
auto it = actionMap.find(choice);
if (it == actionMap.end())
{
cout << "输入无效,请重新输入" << endl;
}
else
{
it->second();
}
}
system("pause");
return 1;
}

使用for_each 遍历。
cpp
map<int, function<void()>> menu = {
{1, doShowAllBooks},
{2, doBorrow},
{3, doBack},
{4, doQueryBooks},
{5, doLoginOut}
};
menu.insert(make_pair(2, doBorrow));
for_each(menu.begin(), menu.end(), [](const pair<int, function<void()>>& pair) {
pair.second();
});
示例3: function的实现原理
Function的实现原理就是 operator() 运算符重载。
cpp
// 实现function类
template<typename Tty>
class myfunction
{
};
/*
// 实现一个参数的function 类,相当于myfunction的部分特例化
template<typename R, typename A1> // R返回值类型,A1参数类型
class myfunction<R(A1)>
{
public:
using PFUNC = R(*)(A1);
myfunction(PFUNC pfunc) :_pfunc(pfunc) {}
R operator()(A1 arg) // arg 实参
{
return _pfunc(arg);
}
private:
PFUNC _pfunc;
};
template<typename R,typename A1,typename A2>
class myfunction<R(A1, A2)> // 返回值(参数类型1,参数类型2)
{
public:
using PFUNC = R(*)(A1, A2);
myfunction(PFUNC pfunc) : _pfunc(pfunc) {}
R operator()(A1 arg1, A2 arg2)
{
return _pfunc(arg1, arg2);
}
private:
PFUNC _pfunc;
};
*/
// 实现多个参数的function
template<typename R,typename...A>
class myfunction<R(A...)>
{
public:
using PFUNC = R(*)(A...);
myfunction(PFUNC pfunc) :_pfunc(pfunc)
{
}
R operator()(A...arg)
{
return _pfunc(arg...);
}
private:
PFUNC _pfunc;
};
void hello(string str) { cout << str << endl; }
int sum1(int a, int b) { return a + b; }
int main()
{
// 使用类库的function
function <void(string)> func1(hello);
func1("hello world!");
myfunction <void(string)> func3(hello);
func3("hello world!");
function<int(int, int)> func2 = sum1;
cout << func2(1, 2) << endl;
myfunction<int(int, int)> func4 = sum1;
func4(1, 2);
system("pause");
return 1;
}
示例4: bind和function实现线程池
在线程池中,通过bind 绑定到线程池中的成员函数,然后在线程类中利用function<void(int)> func, 接受bind绑定的参数。
在给 参数为function的函数传递参数时,实参使用 bind传递。
实现一个简单的线程池:
cpp
// 实现线程池 finction 和 bind
class CMyThread
{
public:
CMyThread(function<void(int)> func, int number)
: m_func(func)
, m_iThreadNumber(number)
{
}
~CMyThread()
{
}
thread start()
{
return thread(m_func, m_iThreadNumber);
}
private:
function<void(int)> m_func;
int m_iThreadNumber;
};
class MyThreadPool
{
public:
MyThreadPool() {}
~MyThreadPool()
{
// 释放thread对象占用的堆资源
for (int i = 0; i < m_vecThreadPool.size(); ++i)
{
delete m_vecThreadPool[i];
}
}
static void func(int i)
{
cout << "这是第 " << i << "个线程" << endl;
std::this_thread::sleep_for(std::chrono::seconds(i));
}
// 开启线程池
void startpool(int size) // 线程池数量size
{
// 创建 size 个线程
for (int i = 0; i < size; ++i)
{
CMyThread* pThread = new CMyThread(func, i);
// 绑定类的成员函数 必须使用bind绑定
//CMyThread* pThread = new CMyThread(bind(&MyThreadPool::threadfunc, this, std::placeholders::_1), i);
m_vecThreadPool.emplace_back(pThread);
m_threadHandler.emplace_back(pThread->start());
m_threadHandler[i].join();
}
}
// 线程执行函数
void threadfunc(int i)
{
cout << "这是第 " << i << "个线程" << endl;
std::this_thread::sleep_for(std::chrono::seconds(i));
}
// void
private:
vector<CMyThread*> m_vecThreadPool;
vector<thread> m_threadHandler;
};
void test()
{
MyThreadPool mypool;
mypool.startpool(5);
/*
这是第 0个线程
这是第 1个线程
这是第 2个线程
这是第 3个线程
这是第 4个线程
*/
}
15.7.5 总结
bind思想,延迟调用,将可调用对象统一格式,保存起来,需要的时候再调用。
function提供了统一的函数管理方式。