温馨提示×

温馨提示×

您好,登录后才能下订单哦!

密码登录×
登录注册×
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》

C++中什么时候使用std::move​

发布时间:2021-11-26 13:53:09 来源:亿速云 阅读:394 作者:iii 栏目:大数据

这篇文章主要讲解了“C++中什么时候使用std::move”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“C++中什么时候使用std::move”吧!

ES.56:只在需要将一个对象显式移动到另外的作用域时使用std::move

Reason(原因)

我们使用move而不是copy是为了避免不必要的重复并提高性能。

移动操作一般会留下一个空对象(C.64),它可能引起误解甚至危险。因此我们努力避免移动左值(它们可能在后续代码中被使用)。

Notes(注意)

如果源数据是右值,移动操作会隐式进行(例如return处理或函数的返回值),在这种情况下进行显式移动操作,会导致代码被漫无目标地的复杂化。相反,编写带返回值的简短函数,这样无论是函数的返回值还是调用侧的接受动作都可以很自然地被优化。

通常情况下,遵循本文档中的准则(包括不要不必要地扩大变量作用域,编写带返回值的简短函数,返回局部变量等)可以帮助消除显式执行std::move的大部分需求。

在显式移动一个对象到另外的作用域时,显式移动是有必要的。特别是:

1.将对象传递给一个“下沉”函数时(接管变量所有权的函数,译者注)

2.实现对象自身移动操作(移动构造函数,移动赋值运算符)和交换操作时

Example, bad(反面示例)

void sink(X&& x);   // sink takes ownership of x

void user()
{
   X x;
   // error: cannot bind an lvalue to a rvalue reference
   sink(x);
   // OK: sink takes the contents of x, x must now be assumed to be empty
   sink(std::move(x));

   // ...

   // probably a mistake
   use(x);
}

通常情况下,std::move()作为为&&参数提供实参。而且在移动之后,应该认为对象已经被移走(参见C.64)并且在赋予新值之前不要获取对象的状态。

void f() {
   string s1 = "supercalifragilisticexpialidocious";

   string s2 = s1;             // ok, takes a copy
   assert(s1 == "supercalifragilisticexpialidocious");  // ok

   // bad, if you want to keep using s1's value
   string s3 = move(s1);

   // bad, assert will likely fail, s1 likely changed
   assert(s1 == "supercalifragilisticexpialidocious");
}
Example(示例)
void sink(unique_ptr<widget> p);  // pass ownership of p to sink()

void f() {
   auto w = make_unique<widget>();
   // ...
   sink(std::move(w));               // ok, give to sink()
   // ...
   sink(w);    // Error: unique_ptr is carefully designed so that you cannot copy it
}
Notes(注意)

std::move()实际上是目标为&&的类型转换;它自己不会移动任何东西,而是将命名对象标记为一个移出操作的候选者。语言已经知道对象可以被移出的一般情况,特别是函数的返回值,因此不要因为多余的std::move导致代码复杂化。

永远不要只是因为听说它更高效就使用std::move。通常不要相信那些脱离具体数据的所谓“高效”。通常不要没有理由地让代码复杂化。永远不要对常量对象调用std::move(),这会不知不觉地产生一个拷贝。

Example, bad(反面示例)

vector<int> make_vector() {
   vector<int> result;
   // ... load result with data
   return std::move(result);       // bad; just write "return result;"
}

永远不要返回局部变量的移动结果;因为语言已经知道这个变量可以作为移动操作的候选,在这种代码中增加move代码不但没有任何帮助,而且对于某些编译器,由于产生了额外的指向局部变量的引用,增加move代码会影响RVO(返回值优化)的正常执行。

Example, bad(反面示例)

vector<int> v = std::move(make_vector());   // bad; the std::move is entirely redundant

如果函数f以传值方式返回结果,永远不要对这个返回值调用move操作,例如X=move(f());语言已经知道返回值是临时变量并且可以进行移出操作。

Example(示例)

void mover(X&& x) {
   call_something(std::move(x));         // ok
   call_something(std::forward<X>(x));   // bad, don't std::forward an rvalue reference
   call_something(x);                    // suspicious, why not std::move?
}

template<class T>
void forwarder(T&& t) {
   call_something(std::move(t));         // bad, don't std::move a forwarding reference
   call_something(std::forward<T>(t));   // ok
   call_something(t);                    // suspicious, why not std::forward?
}
Enforcement(实施建议)
  • 标记针对右值或者已经被语言看作是右值的对象调用std::move的情况。包括std::move(local_variable);,std::move(f()),这里函数f是一个以传值方式返回结果的函数。

  • 标记没有用于处理左值的const S&型重载函数,只有一个处理右值(参数类型:S&&)的函数的情况。

  • 标记向参数传递std::move执行结果的情况,除非参数类型是右值引用类型X&&,或者参数类型为只移动不拷贝类型并且以传值方式传递。

  • 标记对转交引用类型调用std::move的情况(T&&,这里T是模板参数)。

  • 标记std::move运用于指向非常变量的右值引用以外的情况。(前面规则的更普遍形式,以包含非转交参数的情况)

  • 标记std::forward用于右值引用的情况(X&&,这里X是具体类型),转而使用std::move。

  • 标记std::forward用于转交引用之外的情况。(前面规则的更普遍形式,它可以覆盖非移动参数的情况。)

  • 标记对象可能被执行移出操作而且下一个是常量操作(读取对象值,译者注)的情况;哪里应该首先有一个非常量操作(以便修改对象值,译者注),最好是重新设置对象值的赋值操作。

    感谢各位的阅读,以上就是“C++中什么时候使用std::move”的内容了,经过本文的学习后,相信大家对C++中什么时候使用std::move这一问题有了更深刻的体会,具体使用情况还需要大家实践验证。这里是亿速云,小编将为大家推送更多相关知识点的文章,欢迎关注!

    向AI问一下细节

    免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

    c++
    AI