若类重载了函数调用运算符(),就可以像使用函数一样使用该类的对象(像函数调用一样来调用该类的对象)。
class biggerthanzero {
public:
int operator()(int value) const {
if(value<0)
return 0;
return value;
}
};
int i = 200;
biggerthanzero obj;
int result = obj(i); //等价于int result = obj.operator()(i);
class biggerthanzero {
public:
biggerthanzero(int i) {
cout<<"biggerthanzero::biggerthanzero(int)\n";
}
int operator()(int value) const {
if(value<0)
return 0;
return value;
}
};
int i = 200;
biggerthanzero obj(i);//对象定义并初始化,调用的是biggerthanzero构造函数
int result = obj(i); //等价于int result = obj.operator()(i);
若类重载了()运算符(可有多个版本,参数类型或数量有差别即可),则类对象就变成了可调用对象(函数对象)。
int echovalue(int value) {
cout<<value<<endl;
return value;
}
echovalue函数和biggerthanzero类重载的函数调用符具有相同的形参和返回值,叫做调用形式相同。
一种调用形式对应一个函数类型。
int(int)表示接收一个int参数,返回一个int值的函数类型。
可以将可调用对象(函数对象、仿函数)的指针保存起来,方便后续随时调用。
map<string, int(*)(int)> myoper;
myoper.insert({"ev", echovalue});
//系统不会将类biggerthanzero或类对象obj看成函数指针
biggerthanzero obj;
myoper.insert({"bt", biggerthanzero});//error
myoper.insert({"bt", obj}); //error
function,类模板,用来包装可调用对象,统一了类型。
biggerthanzero obj;
function<int(int)> f1 = echovalue;
function<int(int)> f2 = obj;
function<int(int)> f3 = biggerthanzero();
f1(5);
f2(3);
f3(-5);
biggerthanzero obj;
map<string, function<int(int)>> myoper = {
{"ev", echovalue},
{"bt", obj}
};
myoper.insert({"bt2", biggerthanzero()});
myoper["ev"](12);
myoper["bt"](3);
myoper["bt2"](-5);
只要函数是重载的,就无法包装进function中。
可以通过定义函数指针解决。
int echovalue(int value) {
cout<<value<<endl;
return value;
}
void echovalue() {
return;
}
function<int(int)> f1 = echovalue; //error
int(*fp)(int) = echovalue;
function<int(int)> f1 = fp; //ok
void func(const int&abc){}
//abc类型为const int&
template<typename T>
void func(const T&abc){}
func(10);
//T(类型模板参数)的类型是int(与输入参数10和abc类型即const int &有关)
//abc(变量)的类型是const int &
universal reference(forwarding reference,转发引用),万能引用(未定义引用)。
万能引用是一种类型。
void func(int &&tmp) {//tmp为右值引用类型
cout<<tmp<<endl;
}
myfunc(10);//右值作为实参
int i = 100;
//右值引用不能接左值(形参为右值引用类型,实参不能是左值)
myfunc(i); //error
//函数模板推断+函数形参为T&&会出现万能引用
//auto && tmp = ...也是万能引用
//实参传递左值,则万能引用是左值引用(tmp被推断为int &类型,假设传递int)
//实参传递右值,则万能引用是右值引用(tmp被推断为int &&类型,假设传递int)
//T&&(tmp)是万能引用,T不是
template<typename T>
void func(T&& tmp){//tmp类型为T&&,&&和T类型无关
cout<<tmp<<endl;
}
void func(int &¶m){}//右值引用
template<typename T>
void func(T&& tmp){} //万能引用
template<typename T>
void func(vector<T>&& tmp){} //右值引用
template<typename T>
void myfunc(T&& tmp){
tmp = 12;
cout<<tmp<<endl;
}
int i = 100;
myfunc(i);//左值引用
i = 200;
myfunc(std::move(i)); //右值引用
//const剥夺万能引用资格,const T&&只能是右值引用。
template<typename T>
void myfunc(const T&& tmp){//右值引用
tmp = 12;
cout<<tmp<<endl;
}
int i = 100;
myfunc(i); //error
myfunc(std::move(i));//ok
template<typename T>
class mytestc {
public:
void testfunc(T&& x){}//右值引用,不是万能引用
};
mytestc<int> mc;
int i = 100;
mc.testfunc(i); //error
mc.testfunc(std::move(i)); //ok
template<typename T>
class mytestc {
public:
void testfunc(T&& x){}//右值引用,不是万能引用
template<typename T2>
void testfunc2(T2&& x){}//万能引用
};
mytestc<int> mc;
int i = 100;
mc.testfunc2(i); //ok
mc.testfunc2(std::move(i)); //ok
boost
#include
template <typename T>
void myfunc(T& tmprv){
using boost::typeindex::type_id_with_cvr;
cout<<"T = "<<type_id_with_cvr<T>().pretty_name()<<endl;//显示T类型
cout<<"tmprv = "<<type_id_with_cvr<decltype(tmprv)>().pretty_name()<<endl;//显示tmprv类型
}
#include
template <typename T>
void myfunc(const T& tmprv){
using boost::typeindex::type_id_with_cvr;
cout<<"T = "<<type_id_with_cvr<T>().pretty_name()<<endl;//显示T类型
cout<<"tmprv = "<<type_id_with_cvr<decltype(tmprv)>().pretty_name()<<endl;//显示tmprv类型
}
myfunc(10);
//T是类型模板参数,tmprv是函数模板myfunc的形参。
//T = int
//tmprv = int const &
#include
template <typename T>
void myfunc(T& tmprv){
using boost::typeindex::type_id_with_cvr;
cout<<"T = "<<type_id_with_cvr<T>().pretty_name()<<endl;//显示T类型
cout<<"tmprv = "<<type_id_with_cvr<decltype(tmprv)>().pretty_name()<<endl;//显示tmprv类型
}
int i = 18;
myfunc(i);
//T = int
//tmprv = int &
const int j = i;
myfunc(j);
//T = int const
//tmprv = int const &
//myfunc内tmprv不能赋值,即tmprv = 15会报错
const int& k = i;
myfunc(k);
//T = int const
//tmprv = int const &
//myfunc内tmprv不能赋值,即tmprv = 15会报错
#include
template <typename T>
void myfunc(const T& tmprv){
using boost::typeindex::type_id_with_cvr;
cout<<"T = "<<type_id_with_cvr<T>().pretty_name()<<endl;//显示T类型
cout<<"tmprv = "<<type_id_with_cvr<decltype(tmprv)>().pretty_name()<<endl;//显示tmprv类型
}
int i = 18;
myfunc(i);
//T = int
//tmprv = int const &
//myfunc内tmprv不能赋值,即tmprv = 15会报错
const int j = i;
myfunc(j);
//T = int
//tmprv = int const &
//myfunc内tmprv不能赋值,即tmprv = 15会报错
const int& k = i;
myfunc(k);
//T = int
//tmprv = int const &
//myfunc内tmprv不能赋值,即tmprv = 15会报错
#include
template <typename T>
void myfunc(T* tmprv){
using boost::typeindex::type_id_with_cvr;
cout<<"T = "<<type_id_with_cvr<T>().pretty_name()<<endl;//显示T类型
cout<<"tmprv = "<<type_id_with_cvr<decltype(tmprv)>().pretty_name()<<endl;//显示tmprv类型
}
int i = 18;
myfunc(&i);
//T = int
//tmprv = int *
const int *pi = &i;
myfunc(pi);
//T = int const
//tmprv = int const *
template <typename T>
void myfunc(T&& tmprv){...}
int i = 18;
myfunc(i);
//T = int &
//tmprv = int &
const int j = i;
myfunc(j);
//T = int const &
//tmprv = int const &
const int& k = i;
myfunc(k);
//T = int const &
//tmprv = int const &
myfunc(100);
//T = int
//tmprv = int &&
template <typename T>
void myfunc(T tmprv){...}
int i = 18;
myfunc(i);
//T = int
//tmprv = int
const int j = i;
myfunc(j);
//T = int
//tmprv = int
const int& k = i;
myfunc(k);
//T = int
//tmprv = int
char mystr[] = "test";
const char * const point = mystr;
myfunc(point);
//myfunc(mystr);//同上
//T = char const *
//tmprv = char const *
//tmprv = nullptr;//ok
//*tmprv = 'Y';//error
template <typename T>
void myfunc(T tmprv){...}
const char mystr[] = "test";
myfunc(mystr);
//T = char const *
//tmprv = char const *
template <typename T>
void myfunc(T& tmprv){...}
const char mystr[] = "test";
myfunc(mystr);
//T = char const [5]
//tmprv = char const (&)[5]
template <typename T, unsigned L1>
void myfunc(T (&tmprv)[L1]){...}
const char mystr[] = "test";
myfunc(mystr);
//T = char const [5]
//tmprv = char const (&)[5]
template <typename T>
void myfunc(T tmprv){...}
void testFunc(){}
myfunc(testFunc);
//T = void(__cdecl *)(void)
//tmprv = void(__cdecl *)(void)
template <typename T>
void myfunc(T& tmprv){...}
void testFunc(){}
myfunc(testFunc);
//T = void __cdecl (void)
//tmprv = void(__cdecl &)(void)
template<typename T>
void myfunc(T&& tmprv){...}
int i = 18;
myfunc(i);
//T = int &
//tmprv = int &
myfunc(100);
//T = int
//tmprv = int &&
//第一组&是左值引用,第二组&&是右值引用
//左值相遇、左右值相遇、右值相遇、右左值相遇,会发生&符合的合并,折叠。
void myfunc(int& && tmprv){...}
int i = 18;
myfunc(i);
//tmprv = int &
//第一组&是左值引用,第二组&&是右值引用
//左值相遇、左右值相遇、右值相遇、右左值相遇,会发生&符合的合并,折叠。
void myfunc(int& && tmprv){...}
//编译器内部进行折叠(合并),程序不能写三个&&&
//int b = 500;
int& byi = b;
//int& &byy = byi;//error
//int& &byy2 = b;//error
左值-左值,&-&
左值-右值,&-&&
右值-左值,&&-&
右值-右值,&&-&&
存在左值引用,结果就是左值引用。上述组合结果为:
左值引用,&
左值引用,&
左值引用,&
右值引用,&&
转发,把收到的参数以及这些参数相对应的类型(const、左值、右值等)不变地转发给其他函数的函数模板。
万能引用T&&函数模板的形参,可以保存实参的所有类型信息(const、引用等),编译器据此推断出函数模板最终的形参类型。
template<typename F, typename T1, typename T2>
void myFuncTemp(F f, T1 &&t1, T2 &&t2){...}
//实参中来的左值或者右值信息、const信息都保存在t1和t2参数中
void myfunc(int v1, int& v2){
++v2;
cout<<v1+v2<<endl;
}
void myfunc2(int&& v1, int& v2){
cout<<v1+v2<<endl;
}
//youzhi是一个左值(变量永远是左值,有地址),但是它的类型是右值引用(&&youzhi)。
int&& youzhi = 80;
//形参总是左值,即使其类型是右值引用
void ft(int &&ww){}
int j = 2;
//20右值,j左值
myFuncTemp(myfunc2, 20, j);
template<typename F, typename T1, typename T2>
void myFuncTemp(F f, T1 &&t1, T2 &&t2){
f(t1, t2);//t1,t2都是左值(变量,形参本身是左值),即使t1是右值引用
}
//v1中v1需要右值,myfunc2传递左值会报错
void myfunc2(int&& v1, int& v2){
cout<<v1+v2<<endl;
}
专门为转发而存在的函数,要么返回左值,要么返回右值。
实参左值,std::forward后还是左值;
实参右值,形参变为左值,std::forward后转为右值。
std::forward能够保持原始实参的左值或者右值性。
void printInfo(int& t){
cout<<"int&"<<endl;
}
void printInfo(int&& t){
cout<<"int&&"<<endl;
}
template<typename T>
void testF(T&& t){
using boost::typeindex::type_id_with_cvr;
cout<<"T = "<<type_id_with_cvr<T>().pretty_name()<<endl;
cout<<"t = "<<type_id_with_cvr<decltype(t)>().pretty_name()<<endl;
printInfo(t);
printInfo(std::forward<T>(t));
printInfo(std::move(t));
}
TestF(1);
/*
T = int
t = int &&
int&
int&&
int&&
*/
int i = 1;
TestF(i);
/*
T = int &
t = int &
int&
int&
int&&
*/
int ix = 12;
//std::forward(ix)转为右值
//std::forward(ix)转为左值
int&& def = std::forward<int>(ix);
完美转发,将任意的函数名、任意类型参数(个数确定)(保持参数类型不变)传递给函数模板,达到间接调用函数的目的。
声明变量时根据变量初始值的类型自动推断匹配类型。
auto特点:
//auto理解为类型模板参数T,x理解为模板中形参类型。
//T(auto) = int
//tmprv(x) = int
auto x = 27;
auto x = 27;//T = int, t = int
const auto x2 = x;//T = int, t = const int
const auto& xy = x;//T = int, t = const int&
auto xy2 = xy;//T = int, t = int
传值方式针对auto类型会抛弃引用、const等限定符。
auto x = 27;
const auto& xy = x;//const int&
auto& xy3 = xy;//const int &, auto = const int
auto y = new auto(100); // y = int*, auto = int *,auto可用于new操作符
const auto* xp = &x;//const int *, auto = int
auto * xp2 = &x; //int *, auto = int
auto xp3 = &x;// int *,
auto x = 22;
auto&& wnyy0 = 222;//int&&, auto = int
auto&& wnyy1 = x;//int&, auto = int&
const auto x2 = x;//const int
auto&& wnyy3 = x2;//int const &, auto = int const &
const char mystr[] = "test";//const char[5]
auto myarr = mystr;//char const *
auto& myarr2 = mystr;//char const(&)[5]
int a[2] = {1, 2};
auto aauto = a;// int *
void myfunc3(double, int){
cout<<"myfunc3"<<endl;
}
auto tmpf = myfunc3;//void(__cdecl *)(double, int),函数指针
tmpf(1, 2);
auto& tmpf2 = myfunc3;//void(__cdecl &)(double, int),函数引用
tmp2(1, 2);
初始化变量方法:
int x = 10;//c++98
int x2(20);//c++98
int x3 = {30};//c++11
int x4{40};//c++11
auto x = 10;//int
auto x2(20);//c++98
auto x3 = {30};//std::initializer_list,隐式类型转换
auto x4{40};//int
std::initializer_list是c++1中类模板,数组,与vector类型。
auto x5 = {30, 21};
auto x6 = {30, 21, 45.3};//error,类型不一致
template<typename T>
void fautof(T param){}
fautof({12});//error
template<typename T>
void fautof(std::initializer_list<T> param){}
fautof({12});//ok
//c++14支持
auto funca(){
return 12;
}
(1)不能用于函数参数。
void myfunc(auto x, int y){}//error
(2)不能类中普通成员变量。
class CT{
public:
//auto m_i = 12;//error
static const auto m_si = 15;//ok
};
std::map<string, int> mymap;
mymap.insert({"aa", 1});
mymap.insert({"bb", 2});
mymap.insert({"cc", 3});
mymap.insert({"dd", 4});
//for(std::map::iterator iter = mymap.begin(); iter != mymap.end(); ++iter)
for(auto iter = mymap.begin(); iter != mymap.end(); ++iter)
cout<<iter->first<<" = "<<iter->second<<endl;
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;
}
cout<<ftestclass<A>()<<endl;
cout<<ftestclass<B>()<<endl;
decltype用于推导表达式或者变量名的类型,与auto类型。
decltype特点:
const int i = 0;
const int& iy = i;
auto j1 = i;//传值方式推断,引用、const等被抛弃,int
decltype(i) j2 = 15;//const int
decltype(iy) j3 = j2;//const int &
class CT{
public:
int i;
int j;
};
decltype(CT::i) a;//int
CT tmpct;
decltype(tmpct) tmpct2;//CT
decltype(tmpct2.i) mv = 5;//int
int x = 1;
auto&& z = x;//万能引用,x左值,auto是int&,z也是int&
int y = 2;
decltype(z) &&h = y;//int & &&,折叠引用,最终int &h = y;
decltype(8) kkk = 5;//int
int i = 0;
decltype(i) k2;//int
int *pi = &i;
decltype(pi) k;//int *
*pi = 4;
//*pi是左值,*pi是表达式不是变量
//如果表达的结果能够作为赋值语句等号左侧的值(*pi=4;)
//那么decltype后返回的就是一个引用
decltype(*pi) k3 = i;//int &
//(i)表达式,i左值
//decltype((变量))的结果永远是引用
decltype((i)) iy3 = i;//int &
int& iy = i;
decltype(iy+1) j;//int
int testf(){
return 10;
}
decltype(testf()) tmpv = 14;//int
decltype(testf) tmpv2;//int(void),可调用对象
function<decltype(testf)> ftmp = testf;
cout<<ftmp()<<endl;
decltype(testf) * tmpv3;//int(*)(void)
tmpv3 = testf;
cout<<tmpv3()<<endl;
const int&& myfunctest(){
return 0;
}
decltype(myfunctest()) myy = 0;//const int &&
template<typename T>
class CTTMP{
public:
typename T::iterator iter;
void getbegin(T &tmpc){
iter = tmpc.begin();
}
};
using conttype = std::vector<int>;
conttype myarr = {10, 30, 40};
CTTMP<conttype> ct;
ct.getbegin(myarr);
//using conttype = const std::vector;//error,
常量容器定义初始化,begin等返回常量迭代器vector<int>::const_iterator,而不是vector<int>::iterator
c++98通过写类模板偏特化解决
template<typename T>
class CTTMP<const T>{
public:
typename T::const_iterator iter;
void getbegin(const T &tmpc){
iter = tmpc.begin();
}
};
template<typename T>
class CTTMP{
public:
decltype(T().begin()) iter;
void getbegin(T &tmpc){
iter = tmpc.begin();
}
};
class A{
public:
A(){
cout<<"A()"<<endl;
}
~A(){
cout<<"~A()"<<endl;
}
int func() const{
cout<<"func()"<<endl;
return 0;
}
};
A().func();//临时对象,构造函数,func函数,析构函数
(const A()).func();//同上
decltype(A().func()) aaa;//int,无临时对象生成
vector<int> ac;
ac.push_back(1);
ac.push_back(2);
vector<int>::size_type mysize = ac.size();
cout<<mysize<<endl;
decltype(ac)::size_type mysize2 = mysize;
cout<<mysize2<<endl;
typedef decltype(sizeof(double)) mysize_t;
mysize_t abc;//unsigned int
auto func(int a, int b) -> int{}
auto add(int i, int k) -> decltype(i+k) {
return i + k ;
}
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);
}
int i = 19;
cout<<FuncTmp(i)<<endl; //int &tf(int& i)
double d = 28.1;
cout<<FuncTmp(d)<<endl;//double tf(double& d)
//error,tv未定义
//decltype(tf(tv)) FuncTmp(T& tv){...}
(1)函数返回类型
template<typename T>
T& mydouble(T& v){
v *= 2;
return v;
}
int a = 100;
mydouble(a) = 20;
cout<<a<<endl;
template<typename T>
auto mydouble(T& v){//auto推导去掉引用
v *= 2;
return v;
}
template<typename T>
decltype(auto) mydouble(T& v){//保留引用
v *= 2;
return v;
}
int a = 100;
decltype(mydouble(a)) b = a;//int &
(2)变量声明
int x = 1;
const int& y = 1;
auto z = y;//int
decltype(auto) z2 = y;//z2与y完全一致, const int&
(3)(变量)
int i = 10;
decltype((i)) iy3 = i;//int &
decltype(auto) tf1(){
int i = 1;
return i;//int
}
decltype(auto) tf2(){
int i = 1;
return(i);//int&
}
decltype(tf1()) testa = 4;//int
int a = 1;
decltype(tf2()) testb = a;//int&
tf2() = 12;//引用值已释放
void myfunc(int tv){
cout<<tv<<endl;
}
void(*pf)(int) = myfunc;//&myfunc也一样
pf(15);
class TC{
public:
void operator()(int tv){
cout<<tv<<endl;
}
};
TC tc;
tc(20);//tc.operator()(20);
class TC2{
public:
using tfpoint = void(*)(int);
static void mysfunc(int tv){
cout<<tv<<endl;
}
operator tfpoint(){return mysfunc;}//类型转换运算符/函数
};
TC2 tc2;
//先调用tfpoint,再调用mysfunc,这就是一个可调用对象,等价于tc2.operator TC2::tfpoint()(20);
tc2(20);
class TC{
public:
void ptfunc(int tv){
cout<<tv<<endl;
}
};
TC tc;
void(TC:: *mfp)(int) = &TC::ptfunc;//类成员函数指针变量mfp定义并赋初值
(tc. *mfp)(20); //调用成员函数,用到tc对象
可调用(可使用函数运算符“()”)对象,可以类似a(参数1,参数2,…)使用。
可调用对象调用形式统一(名字(参数列表)),定义方法不同。
std::function将可调用对象包装起来,统一可调用对象的调用形式。
void func(int v){cout<<v<<endl;}
std::function<void(int)> f = func;
func(10);
class TC{
public:
static void func(int v){cout<<v<<endl;}
};
std::function<void(int)> f = TC::func;
f(10);
class TC{
public:
void operator()(int v){cout<<v<<endl;}
};
TC tc;
std::function<void(int)> f = tc;
f(10);
void mycallback(int cs, const std::function<void(int)> &f){
f(cs);
}
void runfunc(int x){
cout<<x<<endl;
}
for(int i=0; i<10; i++)
mycallback(i, runfunc);
#include
#include
using namespace std;
class CB
{
std::function<void()> fcallback;
public:
~CB()
{
cout << "CB:~CB()" << endl;
}
CB(const std::function<void()> &f) : fcallback(f)
{
cout << "CB:CB(const std::function &)" << endl;
}
void runcallback()
{
fcallback();
}
};
class CT
{
public:
~CT()
{
cout << "CT:~CT()" << endl;
}
CT()
{
cout << "CT:CT()" << endl;
}
CT(const CT &)
{
cout << "CT::(const CT &)" << endl;
}
void operator()()
{
cout << "CT::operator()()" << endl;
}
};
int main()
{
if (0)
{
CT ct;
const std::function<void()> &f = ct; //CT::(const CT &)
std::function<void()> fcallback = f ; //CT::(const CT &)
}
if (1)
{
CT ct;
CB cb(ct);
cb.runcallback();
}
cout << "Over!" << endl;
return 0;
}
std::bind是函数模板,取代c++98中的bind1st和bind2nd。
std::bind将对象及相关参数绑定在一起,绑定完后可以直接使用,也可以用std::function保存,需要时调用。
std::bind(待绑定的函数对象/函数指针/成员函数指针,参数绑定值1,参数绑定值2,...,参数绑定值n)
std::bind两层意思:
void myfunc1(int x, int y, int z){
cout<<"x="<<x<<",y="<<y<<",z="<<z<<endl;
}
auto bf1 = std::bind(myfunc1, 10, 20, 30);
bf1();
auto bf2 = std::bind(myfunc1, placeholders::_1, placeholders::_2, 30);
bf2(5, 15);
std::bind(myfunc1, placeholders::_1, placeholders::_2, 30)(5, 15);
auto bf3 = std::bind(myfunc1, placeholders::_2, placeholders::_1, 30);
bf3(15, 5);
void myfunc2(int& x, int& y){
x++;
y += 2;
}
int a = 2;
int b = 3;
//bind预先绑定的参数通过值传递,a值传递
//不事先绑定的参数,placeholders通过引用传递,b引用传递
auto bf4 = std::bind(myfunc2, a, placeholders::_1);
bf4(b);//
cout<<a<<endl;//2
cout<<b<<endl;//5
class CQ{
public:
void myfunc(int x, int y){
m_a = x;
}
int m_a = 0;
};
CQ cq;
//cq会生成临时CQ对象
//&cq不会生成临时CQ对象
auto bf5 = std::bind(&CQ::myfunc, cq, placeholders::_1, placeholders::_2);
bf5(10, 20);
std::function<void(int, int)> bf6 = std::bind(&CQ::myfunc, &cq, placeholders::_1, placeholders::_2);
bf6(10, 20);
class CQ{
public:
CQ(){
cout<<"CQ(): "<<this<<endl;
}
CQ(const CQ&){
cout<<"CQ(const CQ&): "<<this<<endl;
}
~CQ(){
cout<<"~CQ(): "<<this<<endl;
}
int m_a = 0;
};
CQ cq;
//成员变量地址当函数一样使用
std::function<int& (void)> bf7 = std::bind(&CQ::m_a, &cq);
bf7() = 7;//cq.m_a = 7
//std::function bf7 = std::bind(&CQ::m_a, cq);
//会调用两次拷贝构造函数,两次析构函数
//一次cq生成临时对象
//一次bind要返回包装后的CQ对象
//会调用一次构造函数,一次拷贝构造函数,两次析构函数
auto rt = std::bind(CT());
rt();
void mycallback(int cs, const std::function<void(int)> &f){
f(cs);
}
void runfunc(int x){
cout<<x<<endl;
}
auto bf = std::bind(runfunc, placeholders::_1);
for(int i=0; i<10; i++)
mycallback(i, bf);
lambda表达式也是一种可调用对象,定义一个匿名函数,并且可以捕获一定范围内的变量。
//lambda表达式一般形式:
//[捕获列表](参数列表) -> 返回类型{函数体;};
auto f = [](int a) -> int {
return a + 1;
};
cout <
lambda表达式特点:
注意:
(1)返回类型后置或者省略(return语句推断返回值类型)。
(2)参数列表可以有默认值。
auto f = [](int a = 8) -> int {
return a + 1;
};
(3)无参时,参数列表甚至’()'都可以省略。
auto f1 = [](){
return 1;
};
auto f2 = []{
return 1;
};
(4)捕获列表[]和函数体{}不能省略。
捕获列表捕获一定范围内的变量。
(1)[]:不捕获任何变量。
int i = 9;
auto f = []{
return i;//error,无法捕获外部变量i
};
static int i = 9;
auto f = []{
return i;//ok,可以直接使用局部静态变量
};
(2)[&]:捕获外部作用域中所有变量,作为引用在函数体中使用。
int i = 9;
auto f = [&]{
i = 5; //&,会修改i值
return i;
};
(3)[=]:捕获外部作用域中所有变量,作为副本(按值)在函数体中使用,可以使用,不能赋值(常量引用?)。
int i = 9;
auto f = [=]{
//i = 5; //error,不能赋值
return i;
};
(4)[this]:用于类中,捕获当前类this指针,让lanbda表达式拥有和当前类成员函数同样的访问权限。使用"&“和”="时,默认添加了此项“this”。
[this]和[=]可以读取,不能修改;[&]可以修改。
class CT{
public:
int m_i = 5;
void myfuncpt(int x, int y){
auto mylambda = [this] {//this,&,=都可以读取成员变量值
return m_i;
};
cout<<mylamba()<<endl;
}
};
CT ct;
ct.myfuncpt(3, 4);
(5)按值捕获和按引用捕获。
class CT{
public:
int m_i = 5;
void myfuncpt(int x, int y){
auto mylambda1 = [this] {return m_i;};//不能使用x,y
auto mylambda2 = [=] {return m_i;};//能使用x,y,不能修改
auto mylambda3 = [&] {return m_i;};//能使用x,y,能修改
auto mylambda4 = [this, x, y] {return m_i;};//能使用x,y,不能修改
auto mylambda5 = [&x, &y] {return x;};//能使用x,y,能修改
}
};
(6)[=, &变量名]:默认按值捕获所有外部变量,其他变量按引用捕获,多个变量逗号隔开。
auto mylambda = [this, &x, y] {return m_i;};
//或者
auto mylambda = [=, &x] {return m_i;};
(7)[&, 变量名]:默认按引用捕获所有外部变量,其他变量按值捕获,多个变量逗号隔开。
auto mylambda = [&, x] {...};
int x = 5;
auto f = [=] {return x;};//5
x = 10;
cout<<f()<<endl;//5
lambda捕获的时刻,已经复制x的值了。
lambda捕获时,已经将所有外部变量值复制一份存储在了lambda表达式变量中。
&引用可以及时访问外部值。
int x = 5;
auto f = [=]() mutable{
x = 6; //无mutable时x不能修改
return x;
}
x = 10;
cout<<f()<<endl;//6
//存在mutable时,即使无参数,()也不能省略
auto f = [=]() mutable{
x = 6; //无mutable时x不能修改
return x;
}
lambda表达式的类型称为闭包类型(CLosure Type),闭包:函数内的函数(可调用对象)。
捕获到的外部变量可看作闭包类型的成员变量,这些成员变量在lambda对象创建时被初始化。
auto aa = [](){};
auto bb = [](){};
using boost::typeindex::type_id_with_cvr;
cout<<"aa="<<type_id_with_cvr<<<decltype(aa)>().pretty_name()<<endl;
cout<<"bb="<<type_id_with_cvr<<<decltype(bb)>().pretty_name()<<endl;
//aa和bb是两个类对象,分属于不同的类。
auto f = []{};//f是一个匿名的类类型对象
std::function<int(int)> fc1 = [](int tv){return tv};
fc1(15);//15
//bind中,第一个参数是函数指针
//第二个参数是tv
std::function<int(int)> fc2 = std::bind([](int tv){return tv}, 16);
//bind绑定死tv为16,这里参数15不起作用,除非bind中使用placeholders::_1
//std::bind([](int tv){return tv}, placeholders::_1);
fc2(15);//16
不捕获任何变量的lambda表达式,可转换为普通的函数指针。
using functype = int(*)(int);
functype fp = [](int tv){return tv};
cout<<fp(17)<<endl;
语法糖是指基于语言现有的特性,构建出一个东西,程序员用起来会很方便。但它没有增加语言的原有功能。lambda表达式可看成定义仿函数闭包(函数中的函数)的语法糖。
vector<int> myvector = {10, 20, 30, 40, 50};
int sum = 0;
for_each(myvector.begin(), myvector.end(), [&sum](int val){
sum += val;
cout<<val<<endl;
});
cout<<sum<<endl;
vector<int> myvector = {10, 20, 30, 40, 50};
auto result = find_if(myvector.begin(), myvector.end(), [](int val){
cout<<val<<endl;
return false;//返回false,find_if会不停遍历myvector,直到返回true
});
vector<int> myvector = {10, 20, 30, 40, 50};
auto result = find_if(myvector.begin(), myvector.end(), [](int val){
if(val>15)
return true;
return false;//返回false,find_if会不停遍历myvector,直到返回true
});
if(result == myvector.end())
cout<<"not find"<<endl;
else
cout<<"find"<<*result<<endl;
lambda表达式优点:代码简洁、灵活、强大,提高开发效率、可维护性等。
引用捕获会导致lambda表达式(闭包)包含绑定到局部变量的引用。
#include
std::vector<std::function<bool(int)>> gv;
void myfunc(){
srand((unsigned)time(NULL));
int tmpvalue = rand() % 6;
gv.push_back([&](int tv){
return tv % tmpvalue == 0;//tmpvalue局部变量,引用悬空
});
}
myfunc();
cout<<gv[0](10)<<endl;//error,tmpvalue已被销毁
c++14允许lambda的形参列表使用auto
gv.push_back([=](auto tv){
return tv % tmpvalue == 0;//tmpvalue会复制一份
});
class AT{
public:
void addItm(){
gv.push_back([=](auto tv){//=实际等于this
return tv % tmpvalue == 0;//tmpvalue实际是this->tmpvalue, 不会复制一份
});
}
int tmpvalue = 7;
};
AT *pat = new AT();
pat->addItem();
delete pat;
cout<<gv[0](10)<<endl;//error,AT类中tmpvalue已被销毁
void addItm(){
auto tmpvalueCopy = tmpvalue;
gv.push_back([tmpvalueCopy](auto tv){
return tv % tmpvalueCopy == 0;//tmpvalueCopy赋值, 不是类AT的成员变量
});
}
void addItm(){
gv.push_back([tmpvalueCopy = tmpvalue](auto tv){//tmpvalue复制到闭包里来
return tv % tmpvalueCopy == 0;
});
}
静态局部变量不能被捕获,但能在lambda表达式中使用。
void myfunc(){
static int tmpvalue;
srand((unsigned)time(NULL));
tmpvalue = rand() % 6;
gv.push_back([](auto tv){//[],static不需要捕获
return tv % tmpvalue == 0;
});
}
void myfunc(){
static int tmpvalue = 4;
gv.push_back([](auto tv){//[],static不需要捕获
cout<<tmpvalue<<endl;
return tv % tmpvalue == 0;
});
tmpvalue++;
}
myfunc();
gv[0](10);//5
myfunc();
gv[0](10);//6
gv[1](10);//6
initializer_list处理非固定参数,但类型相同。
参数数量不固定,参数类型相同。
initializer_list中元素是常值,不能被改变。
initializer_list<int> myarray;
initializer_list<int> myarray2 = {12, 14, 16, 20, 30};
void printvalue(initializer_list<string> tmpstr){
for(auto beg = tmpstr.begin(); beg != tmpstr.end(); ++beg)
cout<<beg->c_str()<<endl;
cout<<tmpstr.size()<<endl;
}
void printvalue2(initializer_list<string> tmpstr){
for(auto& tmpitem : tmpstr)
cout<<tmpitem.c_str()<<endl;
cout<<tmpstr.size()<<endl;
}
printvalue({"aa", "bb", "cc"});
void printvalue(initializer_list<string> tmpstr, int tmpvalue){...}
printvalue({"aa", "bb", "cc"}, 6);
initializer_list<string> myarray3 = {"aa", "bb", "cc"};
initializer_list<string> myarray4(myarray3);
initializer_list<string> myarray5;
myarray5 = myarray4;
class CT{
public:
CT(initializer_list<int> &tmpvalue){}
};
CT ct1 = {10, 220, 30, 40}; //隐式类型转换,explicit可禁止
CT ct2{10, 220, 30, 40};
CT ct3 = CT({10, 220, 30, 40});
int myarray[] = {1, 5, 6, 9};
int myarray2[]{1, 5, 6, 9};
vector<int> myvec = {1, 5, 6, 9};
编译器看到大括号括起来的形如{“aa”, “bb”, “cc”}的内容,一般会转化为std::initializer_list。
可变参数函数必须至少有一个普通参数。
#include
double average(int num, ...){
va_list valist;
double sum = 0;
va_start(valist, num);
for(int i=0; i<num; i++)
sum += va_arg(valist, int);
va_end(valist);
return sum/num;
}
cout<<average(3, 100, 200, 300)<<endl;
void funcTest(const char *msg...){
int csgs = atoi(msg);
va_list valist;
va_start(valist, msg);
for(int paramcount = 0; paramcount < csgs; paramcount++) {
char *p = va_arg(valist, char*);
printf("%d: %s\n", paramcount, p);
}
va_end(valist);
}
注意点:
(1)至少一个有效形参;
(2)省略号形参只能出现在最后的位置;
void myfunc(参数列表, ...);
编译器会对参数列表进行类型检查,函数调用时,省略号对应实参不会进行类型检查。
(3)省略号前逗号可以省略;
void funcTest(const char *msg, ...);
void funcTest(const char *msg...);
(4)多个非可变参时,va_start(valist, msg)绑…前那个形参;
void testFunc(char *pszDest, int DestLen, const char *pszFormat...){}
va_start(valist, pszFormat);
type traits,萃取技术一种可用于编译器根据类型作判断的泛型编程技术。
类型萃取定义了一个编译期间的基于模板的接口,用来查询或者修改类型的属性,可以根据这些类型萃取接口来获取各种信息。
(1)主要类型种类
is_void
is_null_pointer
is_integral
is_floating_point
is_array
is_enum
is_union
is_class
is_function
is_pointer
is_lvalue_reference
is_rvalue_reference
is_member_object_pointer
is_member_function_pointer
(2)复合类型种类
is_fundamental
is_arithmetic
is_scalar
is_object
is_compound
is_reference
is_member_pointer
(3)类型属性
is_const
is_volatile
is_trivial
is_trivially_copyable
is_standard_layout
is_pod
is_literal_type
has_unique_object_representations
is_empty
is_polymorphic
is_abstract
is_final
is_aggregate
is_signed
is_unsigned
is_bounded_array
is_unbounded_array
(4)支持的操作
trivially(平淡的,可有可无的)信息,若构造函数无初始化,就属于trivially。
is_constructible
is_trivially_constructible
is_nothrow_constructible
is_default_constructible
is_trivially_default_constructible
is_nothrow_default_constructible
is_copy_constructible
is_trivially_copy_constructible
is_nothrow_copy_constructible
is_move_constructible
is_trivially_move_constructible
is_nothrow_move_constructible
is_assignable
is_trivially_assignable
is_nothrow_assignable
is_copy_assignable
is_trivially_copy_assignable
is_nothrow_copy_assignable
is_move_assignable
is_trivially_move_assignable
is_nothrow_move_assignable
is_destructible
is_trivially_destructible
is_nothrow_destructible
has_virtual_destructor
is_swappable_with
is_swappable
is_nothrow_swappable_with
is_nothrow_swappable
template<typename T>
void printTraitsInfo(const T& t){
cout<<"type name: "<<typeid(T).name()<<endl;
cout<<"is_void = "<<is_void<T>::value<<endl;
cout<<"is_class = "<<is_class<T>::value<<endl;
cout<<"is_object = "<<is_object<T>::value<<endl;
//普通类,只包含成员变量,不包含成员函数
cout<<"is_pod = "<<is_pod<T>::value<<endl;
cout<<"is_default_constructible = "<<is_default_constructible<T>::value<<endl;
cout<<"is_copy_constructible = "<<is_copy_constructible<T>::value<<endl;
cout<<"is_move_constructible = "<<is_move_constructible<T>::value<<endl;
cout<<"is_destructible = "<<is_destructible<T>::value<<endl;
//虚函数
cout<<"is_polymorphic = "<<is_polymorphic<T>::value<<endl;
cout<<"is_trivially_default_constructible = "<<is_trivially_default_constructible<T>::value<<endl;
//虚析构函数
cout<<"has_virtual_destructor = "<<has_virtual_destructor<T>::value<<endl;
}
class A{
public:
A() = default;
A(A&& ta) = delete;
A(const A& ta) = delete;
virtual ~A(){}
};
class B{
public:
int m_i;
int m_j;
};
class C{
public:
C(int t){}
};
printTraitsInfo(int());
printTraitsInfo(string());
printTraitsInfo(A());
printTraitsInfo(B());
printTraitsInfo(C());
printTraitsInfo(list<int>());