【问题标题】:Why do you use std::move when you have && in C++11? [duplicate]为什么在 C++11 中有 && 时使用 std::move? [复制]
【发布时间】:2013-01-07 07:48:00
【问题描述】:

可能重复:
What is move semantics?

我最近参加了一个 C++11 研讨会,并给出了以下建议。

when you have && and you are unsure, you will almost always use std::move

谁能向我解释为什么你应该使用std::move,而不是一些替代方案和一些不应该使用std::move的情况?

【问题讨论】:

标签: c++ c++11 move-semantics


【解决方案1】:

首先,我要解决的问题可能存在误解:
每当您在代码中看到 T&& t 时(并且 T 是实际类型,而不是模板类型),请记住 t 的值类别是左值(引用),而不是右值(临时)。这很令人困惑。 T&& 仅仅意味着t 是从一个作为右值1 的对象构造,但t 本身 是一个左值,而不是右值。如果它有名字(在这种情况下,t)那么它是一个左值并且不会自动移动,但是如果它没有名字(3+4 的结果)那么它是一个右值并且会自动 如果可以的话,进入它的结果。 类型(在本例中为T&&)与变量的值类别(在本例中为左值)几乎无关。

话虽如此,如果您在代码中写入了T&& t,这意味着您有一个对临时变量的引用,如果您愿意,可以销毁。如果您需要多次访问该变量,您确实想从中获取std::move,否则它将失去它的价值。但是最后一次访问t 时,std::move 是安全的,如果您愿意,它对另一个T 的价值。 (而且 95% 的时间,这就是你想要做的)。所有这些也适用于auto&& 变量。

1.如果T 是模板类型,则T&& 是转发引用,在这种情况下,您最后一次使用std::forward<T>(t) 而不是std::move(t)。见this question

【讨论】:

  • 啊哈!这就是我正在寻找的通用参考案例!
  • @pyCthon:对于通用引用,您希望上次使用 std::forward<T>(t) 而不是 std::move(t)
  • 要记住的一点是,如果您看到一个 named 变量,那么它是否被声明为 && ("rvalue") & 并不重要(reference) 或 `` (copy) - 它总是被认为是一个 lvalue。您可以将真正的右值引用视为只能从函数返回的临时内容(您自己的,或包装在 std::move() 中,简而言之,返回参数)。
  • @Macke:使用通用引用,您不知道它是右值还是左值,因此您不知道是否需要std::movestd::forwardstd::move 相同,如果输入 一个右值,如果输入 一个左值则什么都不做,所以你总是得到正确的机制。见this question
  • @G.Samaras:右值是表达式(函数和数学运算)的未命名结果。例如,myintx + myinty 的结果本身是一个int,但没有名称,所以它将是一个右值。您可以将其传递给期望右值 (&&) 的函数,但不能将其传递给期望左值引用 (&) 的函数。同样,您不能将 myintx 传递给期望右值 (&&) 的函数,因为它有名称。要“取消命名”一个对象,告诉编译器移动该对象是安全的,请使用 std::move 函数。
【解决方案2】:

我发现this article 对一般的右值引用主题非常有启发性。他在最后提到了std::move。这可能是最相关的引用:

我们需要使用std::move,从<utility>——std::move是一种 说,“好吧,老实说我知道我有一个左值,但我想要它 成为右值。”std::move 本身不会移动任何东西;它 只需将左值转换为右值,以便您可以调用移动 构造函数。


假设您有一个如下所示的移动构造函数:

MyClass::MyClass(MyClass&& other): myMember(other.myMember)
{
    // Whatever else.
}

当您使用语句other.myMember 时,返回的值是一个左值。因此代码使用 copy 构造函数来初始化this->myMember。但由于这是一个移动构造函数,我们知道other 是一个临时对象,因此它的成员也是如此。所以我们真的很想使用更高效的 move 构造函数来初始化this->myMember。使用std::move 确保编译器将other.myMember 视为右值引用并调用移动构造函数,如您所愿:

MyClass::MyClass(MyClass&& other): myMember(std::move(other.myMember))
{
    // Whatever else.
}

只是不要在需要保留的对象上使用std::move - 几乎可以保证移动构造函数会破坏传递给它们的任何对象。这就是为什么它们只用于临时的。

希望有帮助!

【讨论】:

    【解决方案3】:

    当您有一个T&& 类型的对象时,它是一个右值,这意味着这个对象可以安全地移动,因为以后没有其他人会依赖它的内部状态。

    由于搬家永远不会比复制更昂贵,因此您几乎总是想搬家。要移动它,您必须使用std::move 函数。

    什么时候应该避免std::move,即使它是安全的?我不会在琐碎的例子中使用它,例如:

     int x = 0;
     int y = std::move(x);
    

    除此之外,我认为没有任何缺点。如果它不会使代码复杂化,恕我直言,应尽可能移动。

    另一个你不想移动的例子是返回值。该语言保证返回值(至少)被移动,所以你不应该写

    return std::move(x); // not recommended
    

    (运气好的话return value optimization命中,比移动操作还要好。)

    【讨论】:

    • Scott Meyers 认为您甚至应该移动原始类型。
    • @FredOverflow 有趣。他是否在他的 C++ 和 Beyond 2012 演讲中解释了这一点?我只能将其用作某种文档,但我不太相信这是个好主意。
    • 他在他的演讲中解释了这一点,我将其链接为对该问题的评论。他说“你应该不假思索地做这件事”。当然,仅仅因为 Scott Meyers 这么说,并不意味着这是每个人都必须坚持而不质疑的终极真理。
    【解决方案4】:

    当您需要将对象的内容“转移”到其他地方时,您可以使用 move ,而无需进行复制。对象也可以使用 std::move 获取临时对象的内容而不进行复制。

    阅读更多来自维基百科的Rvalue references and move constructors

    【讨论】:

    • “签出此链接”不是链接的好标签。你能改进它,以便标签描述链接到的东西吗?
    猜你喜欢
    • 2014-01-17
    • 1970-01-01
    • 2020-12-23
    • 1970-01-01
    • 1970-01-01
    • 2019-09-26
    • 2019-07-09
    相关资源
    最近更新 更多