网站模板系统,建筑人才网官网平台,常见的erp系统,郑州外贸网站推广完美转发1. 在函数模板中#xff0c;可以将自己的参数“完美”地转发给其它函数。所谓完美#xff0c;即不仅能准确地转发参数的值#xff0c;还能保证被转发参数的左、右值属性不变。2. C11标准引入了右值引用和移动语义#xff0c;所以#xff0c;能否实现完美转发…完美转发1. 在函数模板中可以将自己的参数“完美”地转发给其它函数。所谓完美即不仅能准确地转发参数的值还能保证被转发参数的左、右值属性不变。2. C11标准引入了右值引用和移动语义所以能否实现完美转发决定了该参数在传递过程使用的是拷贝语义(调用拷贝构造函数)还是移动语义 (调用移动构造函数)。1. 如果模板中 (包括类模板和函数模板)函数的参数书写成为T 参数名 那么函数既可以接受左值引用又可以接受右值引用。2. 提供了模板函数std::forwardT(参数)用于转发参数如果参数是一个右值转发之后仍是右值引用;如果 参数是一个左值转发之后仍是左值引用move在我的另一篇文章《C之右值引用、移动构造函数》提到左值不能初始化右值引用。那么我们能不能用左值初始化一个右值引用呢这里C给我们了一个函数move();move()函数将左值转换为右值实现对象资源的转移。下面看个例子void test2()
{vectorint v;for (int i  0; i  10; i)v.push_back(i);vectorintvv  v;  //拷贝cout  v  ;for (auto x : v)cout  x   ;cout    v.addr  v  endl;cout  vv ;for (auto x : vv)cout  x   ;cout   vv.addr  vv  endl;cout  after move v to vvv  endl;vectorintvvv  move(v);cout  vvv;for (auto x : vvv)cout  x   ;cout  vvv.addr  vvv  endl;cout  v  ;for (auto x : v)cout  x   ;cout    v.addr  v  endl;
}forward右值引用类型是独立于值的一个右值引用作为函数参数的形参时在函数内部转发该参数给内部其他函数时它就变成一个左值因为没有实名的右值被编译器视为左值并不是原来的类型了。如果需要按照参数原来的类型转发到另一个函数可以使用 C11 提供的 std::forward () 函数该函数实现的功能称之为完美转发。#includeiostream
using namespace std;
templatetypename T
void PrintT(T t)
{cout  lvalue  endl;
}
templatetypename T
void PrintT(T t)
{cout  rvalue  endl;
}
templatetypename T
void TestForward(T v)
{PrintT(v);PrintT(move(v));PrintT(forwardT(v));cout  endl;
}
int main()
{TestForward(1);int x  1;TestForward(x);TestForward(forwardint(x));TestForward(forwardint(x));TestForward(forwardint(x));return 0;
}分析一下TestForward(1);实参为右值,T-右值引用 1. PrintT(v);已经实名的右值编译器当成左值处理,实参-左值 2. PrintT(move(v));move将左值转换为右值,实参-右值 3. PrintT(forwardT(v));模板参数为右值引用,最终得到一个右值,实参-右值TestForward(x);实参为左值,T-左值引用 1. PrintT(v);实参-左值 2. PrintT(move(v));move将左值转换为右值,实参-右值 3. PrintT(forwardT(v));模板参数为左值引用,最终得到一个左值,实参-左值TestForward(forwardint(x));模板参数类型为int-右值,最终得到右值,T-右值引用 1. PrintT(v);已经实名的右值编译器当成左值处理,实参-左值 2. PrintT(move(v));move将左值转换为右值,实参-右值 3. PrintT(forwardT(v));模板参数为右值引用,最终得到一个右值,实参-右值TestForward(forwardint(x));模板参数类型为左值引用,最终得到一个左值,T-左值引用 1. PrintT(v);实参-左值 2. PrintT(move(v));move将左值转换为右值,实参-右值 3. PrintT(forwardT(v));模板参数类型为左值引用,最终得到一个左值,实参-左值TestForward(forwardint(x));模板参数类型为右值引用,最终得到一个右值,T-右值引用 1. PrintT(v);已经实名的右值编译器当成左值处理,实参-左值 2. PrintT(move(v));move将左值转换为右值,实参-右值 3. PrintT(forwardT(v));模板参数类型为右值引用,最终得到一个右值,T-右值引用移动语义如果一个对象中有堆区资源需要编写拷贝构造函数和赋值函数实现深拷贝。深拷贝把对象中的堆区资源复制了一份如果源对象(被拷贝的对象)是临时对象拷贝完就没什么用了这样会造成没有意义的资源申请和释放操作。如果能够直接使用源对象拥有的资源可以节省资源申请和释放的时间。C11 新增加的移动语义就能够做到这一点。下面给出示例:我们当时提到的深浅拷贝深拷贝解决了浅拷贝造成的内存泄露问题但是反复的构造和释放对象拉低了程序的效率。所以我们在想是否能直接使用原对象的资源这样可以大大提高效率#includeiostream
using namespace std;
class AA
{
public:int* m_data  nullptr;AA()  default;void alloc(){m_data  new int;memset(m_data, 0, sizeof(int));    //初始化已分配的内存}AA(const AA a)    //拷贝构造函数{cout  调用了拷贝构造函数  endl;if (m_data  nullptr)    //如果没有分配内存就分配alloc();memcpy(m_data, a.m_data, sizeof(int));}AA(AA a)    //移动构造函数{cout  调用了移动构造函数  endl;if (m_data ! nullptr)    //如果分配内存释放掉delete m_data;m_data  a.m_data;a.m_data  nullptr;}AA operator(const AA a){cout  调用了赋值函数  endl;if (this  a)    //避免自我复制return *this;if (m_data  nullptr)    //如果没有分配内存就分配alloc();memcpy(m_data, a.m_data, sizeof(int));return *this;}AA operator(AA a){cout  调用了移动赋值函数  endl;if (this  a)    //避免自我复制return *this;if (m_data ! nullptr)    //如果分配内存释放掉delete m_data;m_data  a.m_data;a.m_data  nullptr;return *this;}~AA(){if (m_data ! nullptr)delete m_data;m_data  nullptr;}
};测试案例1void test1()
{cout  test1-begin  endl;AA a1;a1.alloc();*a1.m_data  3;cout  a1.m_data  *a1.m_data  endl;AA a2(a1);cout  a2.m_data  *a2.m_data  endl;AA a3;a3  a1;cout  a3.m_data  *a3.m_data  endl;cout  test1-end  endl  endl;
}这是一组普通的测试案例返回结果注意对于一个左值会调用拷贝构造函数但是有些左值是局部变量生命周期也很短能不能也移动而不是拷贝呢?C11为了解决这个问题提供了std:move0方法来将左值转义为右值从而方便使用移动语义。它其实就是告诉编译器虽然我是一个左值但不要对我用拷贝构造函数用移动构造函数吧。左值对象被转移资源后不会立刻析构只有在离开自己的作用域的时候才会析构如果继续使用左值中的资源可能会发生意想不到的错误。如果没有提供移动构造/赋值函数只提供了拷贝构造/赋值函数编译器找不到移动构造/赋值函数就去寻找拷贝构造/赋值函数。C11中的所有容器都实现了移动语义避免对含有资源的对象发生无谓的拷贝。移动语义对于拥有资源如内存文件句柄的对象如果是基本类型使用移动语义没有意义。测试案例2void test2()
{cout  test2-begin  endl;AA a1;a1.alloc();*a1.m_data  3;AA a6(std::move(a1));cout  a6.m_data  *a6.m_data  endl;AA a7;a7  std::move(a1);cout  a7.m_data  *a7.m_data  endl;cout  test2-end  endl  endl;
}返回结果test2-begin调用了移动构造函数a6.m_data3调用了移动赋值函数//程序到这里崩溃了为什么呢这里就是因为a1被转移资源后下面再次对a1进行转移资源导致报错。测试案例3void test3()
{cout  test3-begin  endl;auto f  [] {AA aa; aa.alloc(); *aa.m_data  8; return aa; };AA a4  f();cout  a4.m_data  *a4.m_data  endl;AA a5;a5  f();cout  a5.m_data  *a5.m_data  endl;cout  test3-end  endl  endl;
}返回结果test3-begin调用了移动构造函数a4.m_data8调用了移动构造函数调用了移动赋值函数a5.m_data8test3-end