zoukankan      html  css  js  c++  java
  • [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基础上运行,至此,完美转发均完全实现了。

                                                                                                                                                                         

  • 相关阅读:
    tensorflow 2.0 学习 (十) 拟合与过拟合问题
    tensorflow 2.0 学习 (九) tensorboard可视化功能认识
    tensorflow 2.0 学习 (八) keras模块的认识
    tensorflow 2.0 学习 (七) 反向传播代码逐步实现
    tensorflow 2.0 学习 (六) Himmelblua函数求极值
    tensorflow 2.0 学习 (五)MPG全连接网络训练与测试
    arp协议简单介绍
    Pthread spinlock自旋锁
    线程和进程状态
    内核态(内核空间)和用户态(用户空间)的区别和联系·
  • 原文地址:https://www.cnblogs.com/hustcser/p/2946271.html
Copyright © 2011-2022 走看看