• [006]Reference in C++C++11及其完美转发


    其实同事已经对C++0X中的完美转发有了很好的理解,在这里我只是在自己的基础上衍生的一些理解加上去

    同事博客C++完美转发地址参考:http://www.cnblogs.com/alephsoul-alephsoul/archive/2013/01/10/2853900.html

    引言

      其实完美转发,其实就是在调用了函数的基础上,能够相当于调用第二个函数,那么这个函数中的参数就完美转发给了第二个函数。

    转发方式一:非常量左值引用

     1 #include <iostream>
     2 using namespace std;
     3 
     4 void F(int a1, int a2, int a3) {
     5     cout << a1 + a2 + a3 <<endl;
     6 }
     7  
     8 template<class A1, class A2, class A3>
     9 void G(A1& a1, A2& a2, A3& a3) {
    10    return F(a1, a2, a3);
    11 }
    12 
    13 int main() {
    14     int i,j,k;
    15     G(i, j, k);
    16     G(1, 2, 3);
    17     F(1, 2, 3);
    18 
    19     system("pause");
    20 }

    16行代码中G(1,2,3)是不能编译通过的,因为我们无法接收非常量右值的参数。

    但是,代码中15行是完全可以编译通过的,虽然此时的i,j,k均是左值,但此时的G(i, j, k)实际上就是取了i,j,k的任意值相加。

    F(1,2,3)可以完全略去不讲。

    转发方式二:常量左值引用

     1 #include <iostream>
     2 using namespace std;
     3 
     4 void F(int& a1, int& a2, int& a3) {
     5     cout << a1 + a2 + a3 <<endl;
     6 }
     7  
     8 template<class A1, class A2, class A3>
     9 void G(A1 const & a1, const A2& a2,const A3& a3) {
    10    return F(a1, a2, a3);
    11 }
    12 
    13 int main() {
    14     int i,j,k;
    15     G(i, j, k);
    16     G(1, 2, 3);
    17 
    18     system("pause");
    19 }

    此时的15、16行均会出错,原因就在于无法将一个常量左值引用转发给一个非常量左值引用。

    虽然15行中传入的是非常量左值,但是G函数可供接收的却是常量左值引用,因此失败。

    转发方式三:非常量左值引用+常量左值引用

     1 #include <iostream>
     2 using namespace std;
     3 
     4 void F(int a) {
     5     cout << a <<endl;
     6 }
     7  
     8 template<class A>
     9 void G(A &a) {
    10    return F(a);
    11 }
    12 
    13 template<class A>
    14 void G(const A &a) {
    15    return F(a);
    16 }
    17 
    18 int main() {
    19     int i;
    20     G(i);
    21     G(1);
    22 
    23     system("pause");
    24 }

    通过函数重载来让转发时找到合适的转发方式,这种方式倒是实现了转发,但是却非常的不“完美”,详细请看同事的博客。

    需要注意的是,第20行中,G(i)由于i是非常量左值,因此进入的是G(A &a)函数,但是由于i未被初始化,所以出现的是一个任意值。

    转发方式四:常量左值引用+const_cast

     1 #include <iostream>
     2 using namespace std;
     3 
     4 void F(int a) {
     5     cout << a <<endl;
     6 }
     7  
     8 template<class A>
     9 void G(const A &a) {
    10    return F(const_cast<A &>(a));
    11 }
    12 
    13 int main() {
    14     int i;
    15     G(i);
    16     G(1);
    17 
    18     system("pause");
    19 }

    整个过程编译是没有问题的,但是通过const_cast,却将传入的const属性去除了,这样在调用G()函数后,我们就可以通过F()函数来修正传入的常量左值和常量右值了。当一个参数的属性都被修正了,还叫“完美”么?

    转发方式五:非常量左值引用+修改的参数推倒规则转发

     1 #include <iostream>
     2 using namespace std;
     3 
     4 void F(int a) {
     5     cout << a <<endl;
     6 }
     7  
     8 template<class A>
     9 void G(A &a) {
    10    return F(a);
    11 }
    12 
    13 void G(const long &a) {
    14     return F(a);
    15 }
    16 
    17 int main() {
    18     int i;
    19     G(i);
    20     G(1);
    21 
    22     system("pause");
    23 }

    奈何啊,模板编程的参数推倒规则不懂啊,这里理解起来非常困难。

    按照我的理解,其实修正后的模板规则跟函数重载差不多,系统会自动匹配最佳的规则。

    转发方式六:右值引用

     1 #include <iostream>
     2 using namespace std;
     3 
     4 void F(int a) {
     5     cout << a <<endl;
     6 }
     7  
     8 template<class A>
     9 void G(A &&a) {
    10    return F(a);
    11 }
    12 
    13 int main() {
    14     int i;
    15     G(i);
    16     G(1);
    17 
    18     system("pause");
    19 }

    第9行的代码就是C++11中提出的右值引用新标准,如果你在VS08中编译的话是不会通过的,只有在10以上的才可以。

    同事的博客上写的是不能将一个左值传递给一个右值引用,这句话本身是没有问题的,但是在代码15行中,i是作为一个左值的出现,所以需要注意的是传到G(A &&a)中的参数其实是一个为2的右值,在G()函数中并没有保证传递参数的属性,所以也算不上完美转发。

     转发方式七:右值引用+修改的参数推倒规则转发

    1、T& + & = T&
    2、T& + && = T&
    3、T&& + & = T&
    4、T或T&& + && = T&&

    上述的4个引用叠加规则的理解至关重要,修改后的针对右值引用的参数推导规则为:若函数模板的模板参数为A,模板函数的形参为A&&,则可分为两种情况讨论:

    1、若实参为T&,则模板参数A应被推导为引用类型T&。(由引用叠加规则第2点T& + && = T&和A&&=T&,可得出A=T&)

    2、若实参为T&&,则模板参数A应被推导为非引用类型T。(由引用叠加规则第4点T或T&& + && = T&&和A&&=T&&,可得出A=T或T&&,强制规定A=T)

    基于以上的分析,我们可以写出以下的代码:

     1 #include <iostream>
     2 using namespace std;
     3 
     4 void F(int a) {
     5     cout << a <<endl;
     6 }
     7  
     8 template<class A>
     9 void G(A &&a) {
    10    return F(static_cast<A &&>(a));
    11 }
    12 
    13 int main() {
    14     int i = 2;
    15     G(i);
    16     G(1);
    17 
    18     system("pause");
    19 }

       当传给f一个左值(类型为T)时,由于模板是一个引用类型,因此它被隐式装换为左值引用类型T&,根据推导规则1,模板参数A被推导为T&。这样,在f内部调用F(static_cast<A &&>(a))时,static_cast<A &&>(a)等同于static_cast<T& &&>(a),根据引用叠加规则第2点,即为static_cast<T&>(a),这样转发给g的还是一个左值。

       当传给f一个右值(类型为T)时,由于模板是一个引用类型,因此它被隐式装换为右值引用类型T&&,根据推导规则2,模板参数A被推导为T。这样,在G内部调用F(static_cast<A &&>(a))时,static_cast<A &&>(a)等同于static_cast<T&&>(a),这样转发给F的还是一个右值(不具名右值引用是右值)。

       这样看来,无论左值还是右值,都实现了完美的转发,所以这种被称为是完美转发。

       在C++11中,专门提供了一个函数模板来实现完美转发,他就是forward。

     1 #include <iostream>
     2 using namespace std;
     3 
     4 void F(int a) {
     5     cout << a <<endl;
     6 }
     7  
     8 template<class A>
     9 void G(A &&a) {
    10    return F(forward<A &&>(a));
    11 }
    12 
    13 int main() {
    14     int i = 2;
    15     G(i);
    16     G(1);
    17 
    18     system("pause");
    19 }

    上述代码需要在VS10基础上运行,至此,完美转发均完全实现了。

                                                                                                                                                                         

  • 相关阅读:
    [51nod1474]宝藏图
    web h5常用代码总结
    ionic app 热更新
    ionic3——ion-scroll无法使用scrollTo的问题
    git操作
    uniapp开发
    uniapp 之navigateTo:fail page 跳转路径不对
    微信小程序之登录用户不是该小程序的开发者
    ionic slide组件使用
    ionic使用自定义icon
  • 原文地址:https://www.cnblogs.com/hustcser/p/2946271.html
Copyright © 2020-2023  润新知