C++不要对函数局部变量返回值添加std::move()

C++不要对函数返回值添加std::move()

References

  1. C++ 函数返回局部变量的std::move()问题?
  2. Return Statement Copy elision

Summary

  1. 编译器会进行返回值优化——复制省略(copy elision),局部对象直接创建在了需要函数返回值的地方。仅创建局部对象时调用构造函数。

  2. 如果对局部对象返回值添加std::move(),调用了构造函数后,再先调用移动构造函数,再调用析构函数,反而加大了开销!

#include <iostream>
using namespace std;
class TestMove{
public:
    TestMove(){
        cout<<"Constructor"<<endl;
    }
    TestMove(const TestMove &&other){
        cout<<"Move constructor"<<endl;
    }
    TestMove(const TestMove &other){
        cout<<"Copy constructor"<<endl;
    }
    ~TestMove(){
        cout<<"Deconstrcutor"<<endl;
    }
};

TestMove func(){
    cout<<"no Move"<<endl;
    TestMove testMove;
    return testMove;
}

TestMove func_with_move(){
    cout<<"with move"<<endl;
    TestMove testMove;
    return std::move(testMove);
}

int main(){
    TestMove tm1 = func();
    cout<<endl;
    TestMove tm2 = func_with_move();
    cout<<endl;
    return 0; 
}

no Move
Constructor

with move
Constructor
Move constructor
Deconstrcutor

Deconstrcutor
Deconstrcutor
  1. 确定会返回值优化的场景
  • URVO(Unnamed Return Value Optimization):函数的所有执行路径都返回同一个类型的匿名变量
User create_user(const std::string &username, const std::string &password) {
    if (find(username)) return get_user(username);
    else if (validate(username) == false) return create_invalid_user();
    else User{username, password};
}

// 作者:神奇先生
// 链接:https://www.zhihu.com/question/57048704/answer/151446405
// 来源:知乎
// 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
  • NRVO(Named Return Value Optimization):函数的所有路径都返回同一个非匿名变量,比如
User create_user(const std::string &username, const std::string &password) {
    User user{username, password};
    if (find(username)) {
        user = get_user(username);
        return user;
    } else if (user.is_valid() == false) {
        user = create_invalid_user();
        return user;
    } else {
        return user;
    }
}

//作者:神奇先生
//链接:https://www.zhihu.com/question/57048704/answer/151446405
//来源:知乎
//著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

Note

首先 URVO 在 C++17 是强制的。不过 NRVO 不是强制,意味着有时不这么优化也是允许的。其次,若 return 的表达式是符合返回类型的左值,且编译器没有进行复制省略,那么标准(C++11 开始)也要求编译器先试图把表达式当右值,优先匹配移动构造函数(再匹配通常的复制构造函数),若失败的话则再将其当左值,匹配接受非 const 引用的复制构造函数。所以按照标准,上面的 std::move(a) 是不必要的(除非你希望强制调用移动构造函数),编译器在必要时会做同样的处理。

作者:暮无井见铃
链接:https://www.zhihu.com/question/57048704/answer/151453824
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

posted on 2023-01-12 14:40  七昂的技术之旅  阅读(955)  评论(0编辑  收藏  举报

导航