C++移动构造、std::move、std::forward

本文详细介绍了C++中的移动构造函数、右值引用以及std::move的作用。通过实例解释了拷贝构造与移动构造在对象创建和赋值过程中的区别,展示了如何通过移动构造优化性能。同时,讨论了引用折叠和std::forward在函数模板中的应用,以及它们如何帮助实现完美转发。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

C++移动构造、move、右值引用

1、拷贝构造

用一个临时对象(即将消亡)去初始化、赋值另一对象时,编译器会优先调用移动构造函数。

class Cla_demo{
public:
    Cla_demo(const char* str):size(5)
    {
        num = new char[5];
        memcpy(num,str,5);
        cout<<"construct!"<<endl;
    }
    Cla_demo(const Cla_demo &d){
         num = new char[5];
        memcpy(num,d.num,5); 
        cout<<"copy construct!"<<endl;
    }
    ~Cla_demo(){
        cout <<"num " << static_cast<void *>(num) <<endl;
        cout <<this <<endl;
        cout<<"class destruct!"<<endl;
    }
private:
    char *num;
    int size;
};

上面的类中无移动构造函数,当main函数如下时:

Cla_demo  func(){
    return (Cla_demo("abcd"));
}
int main(){
   // Cla_demo a(func());
    Cla_demo a = func();
    cout << "-----------" << endl;
    return 0;
}

编译器执行的操作为:
1、在func函数栈上构造一个demo对象 f : Cla_demo(“abcd”)
2、return时执行拷贝构造,将
f拷贝到main函数栈中,创建出对象 b,这时会析构f,因为func结束了。
3、此时的b(函数返回值),会再次执行拷贝构造,拷贝给a,然后b被析构。
4、main结束,a被析构。

在这里插入图片描述

2 、移动构造

加入移动构造函数:

    Cla_demo(Cla_demo &&d){
        num = d.num;
        d.num = NULL;
        cout<<"move construct!"<<endl;
    }

此时编译器执行的操作为:
1、在func函数栈上构造一个demo对象 f : Cla_demo(“abcd”)
2、func即将结束时(f即将被析构),执行移动构造,在main函数栈上创建吧,并将f.str的地址空间移动给b.str。
3、 b(函数返回值)是一个右值,因此Cla_demo a(func()) 或 Cla_demo a = func() 会执行移动构造,构造出a,并将b.str的空间移交到a.str。b被析构。

在这里插入图片描述

3 、move

std::move会将一个左值转换为右值,可以避免不必要的拷贝。

int main(){
   // Cla_demo a(func());

    Cla_demo a("abcd");   //1
    Cla_demo c(a);		  //2
    Cla_demo d(move(a));  //3
    cout << "-----------" << endl;
    return 0;
}

1、执行构造函数。
2、执行拷贝构造。
3、执行移动构造。a.str变为空。

增加如下接口:

   Cla_demo & operator=(Cla_demo &d)
    {
        num = d.num;
        d.num = NULL;
        cout<<"copy = !"<<endl;
        return *this;
    }

    Cla_demo & operator=(Cla_demo &&d)
    {
        num = d.num;
        d.num = NULL;
        cout<<"move = !"<<endl;
        return *this;
    }

然后修改main:

    Cla_demo a("abcd");
    Cla_demo c;
    c =  move(a);

会依次执行 有参构造,无参构造,operator=(Cla_demo &&d) 。因此可见,move将a(左值)转化成了右值。

c = func();

上面代码会执行:
有参构造。
移动构造。
operator=(Cla_demo &&d) 。

4、引用折叠forward

万能引用:T &&

规则:
&& & --> &
& && --> &
& & --> &
&& && --> &&

// 完美转发原型:  
T&& forward(T&& t) { return static_cast<T&&>(t); }    

// 用法:   template<typename T>  
void func1(T && val) { func2(std::forward<T>(val)); }  

// 当传入左值引用   
void func1(T& && val) { func2(static_cast<T& &&>(val)); }  
// 引用折叠后:   
void func1(T& val) { func2(static_cast<T&>(val)); }   
 
// 当传入右值引用   
void func1(T&& && val) { func2(static_cast<T&& &&>(val)); } 
// 引用折叠后: 
void func1(T&& val) { func2(static_cast<T&&>(val)); }     

T&& 为形参时,无论传入左值还是右值,后续使用都会退化为左值。如下会出现编译错误:

void RFn(int&& arg){

}

template<typename T>
void ProxyFn(T&& arg){
      RFn(arg);
}

void main(){
     ProxyFn(1);
}

修改为:

template<typename T>
void ProxyFn(T&& arg){
    RFn(std::forward<T>(arg));
}

在万能引用中,编译器有一个规则,如果传入的是左值,则模板类型会被推导成左值引用类型; 传入的是右值,则模板类型就是值的类型

// FUNCTION TEMPLATE forward
template <class _Ty>
_NODISCARD constexpr _Ty&& forward(
    remove_reference_t<_Ty>& _Arg) noexcept { // forward an lvalue as either an lvalue or an rvalue
    return static_cast<_Ty&&>(_Arg);
}

template <class _Ty>
_NODISCARD constexpr _Ty&& forward(remove_reference_t<_Ty>&& _Arg) noexcept { // forward an rvalue as an rvalue
    static_assert(!is_lvalue_reference_v<_Ty>, "bad forward call");
    return static_cast<_Ty&&>(_Arg);
}

如果传入的是左值,则匹配上面的函数模板,传入右值匹配下面的。
左值时_Ty被推导为_Ty&,则static_case<_Ty& &&> 被推导为 static_cast<_Ty&>
同理,传入右值时_Ty被推导为_Ty&&,则static_case<_Ty&& &&> 被推导为 static_cast<_Ty&&>
看如下例子:

template<typename T>
void print(T& t){
    cout << "lvalue" << endl;
}
template<typename T>
void print(T&& t){
    cout << "rvalue" << endl;
}
 
template<typename T>
void TestForward(T & v){
    cout << "TestForward(T & v)" <<endl;
    print(v);					
    print(std::forward<T>(v));
    print(std::move(v));
}

template<typename T>
void TestForward(T && v){
    cout << "TestForward(T && v)" <<endl;
    print(v);
    print(std::forward<T>(v));
    print(std::move(v));
}

template<typename T>
struct mytype{
  T a ;
  T b;  
};

int main()
{
  struct mytype<int> mt{1,2};
    TestForward(mt);
    TestForward(10);
    TestForward(mytype(5));
}

输出为:
TestForward(T & v)
lvalue
rvalue
rvalue
TestForward(T && v)
lvalue
rvalue
rvalue
TestForward(T && v)
lvalue
rvalue
rvalue

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值