万能引用(universal reference/forwarding reference:转发引用)

1类型区别基本含义

1
2
3
4
template<typename T>
void func(const T& abc){}

func(10); //T:int,abc=const int&

2universal reference/万能引用/未定义引用基本认识

  • 结论:万能引用是一种类型
  • 右值引用(全程:右值引用类型)是用&&表示,右值引用绑定在右值上
1
2
3
4
5
6
7
8
9
void myfunc(int&& tmpv) {
cout << tmpv << endl;
}


int&& rv = 1000; //rv是右值引用
myfunc(10);
int i = 100; //i是左值
//nmsp1::myfunc(i); //编译出错,右值引用不能绑定左值

如果我们想一个函数既能接收左值引用又能接收右值引用

1
2
3
4
5
6
7
8
9
10
template<typename T>
void myfunc1(T&& tmpv) {
//注意 &&是属于tmprv类型的一部分,不是T类型的一部分(&&和T)没有关系
cout << tmpv << endl;
//万能引用能接收左值和右值
//万能引用也称为未定义类型
}

myfunc1(10);
myfunc1(i);

万能引用离不开两种语境

  1. 必须是函数模板

  2. 必须是发生模板类型推断并且函数模板形参长这样:T&&;

  3. 如果参数传递了一个整型左值传递给形参,tmprv的类型最终会被推断为int&类型

  4. 如果参数传递了一个整型右值传递给形参,tmprv的类型最终会被推断为int&&类型

题目:

1
2
3
4
void func(int&& param) {...}//右值引用

template<typename T>
void func(T&& param){...}//是万能引用

std::move把左值转换为右值

1
2
3
4
5
6
7
template<typename T>
void func2(std::vector<T>&& param) {}//右值引用


std::vector<int> aa = { 1 };
//nmsp1::func2(aa);//错误:无法将左值转换为右值
nmsp1::func2(std::move(aa) );//转换为右值

什么情况才是万能引用

  1. 一个是函数模板中用作函数参数的类型推断(参数中主要涉及类型推断)T&&

  2. auto&& tmpvalue=….也是万能引用

  3. 其他情况的&&的情形的都是右值引用

1
2
3
4
5
6
7
8
9
10
11
12
template<typename T>
void myfunc3(T&& tmpv) {//
tmpv = 12;
//不管tmpv的类型是左值引用还是右值引用,都可以给tmprv赋值,因为tmprv本身是个左值
cout << tmpv << endl;
}

myfunc3(i);
//左值被传递,因为tmprv是个左值引用,也就是int&,最终i=12
i = 200;
myfunc3(std::move(i));
//右值被传递,因此是tmprv是个右值引用,也就是int&&,最终i变为12

万能引用资格的剥夺与辨认

  • 剥夺,const会剥夺一个引用成为万能引用的资格,被打回右值成为右值引用

    1
    2
    3
    4
    5
    6
    7
    template<typename T>
    void myfunc4(const T&& tmpv) {//有const修饰
    cout << tmpv << endl;
    }

    //nmsp1::myfunc4(i);错误,只能传递右值
    myfunc4(std::move(i));
  • 辨认

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    template<typename T1>
    class mytest {
    public:
    void testfunc(T1&& x){}
    //这个不是万能引用,

    template<typename T2>
    void testfunc2(T2&& x) {}//x类型是万能引用类型
    };


    nmsp2::mytest<int> mc;
    //mc.testfunc(i);//错误
    mc.testfunc(std::move(i));

    //因为testfunc成员函数本身没有涉及到类型推断
    mc.testfunc2(i);
    mc.testfunc2(2);