【问题标题】:Is it useless to declare a local variable as rvalue-reference, e.g. T&& r = move(v)?将局部变量声明为右值引用是否没用,例如T&& r = 移动(v)?
【发布时间】:2013-09-16 23:04:20
【问题描述】:

你们能不能给我一个在特定情况下的说明性例子来证明以下陈述是有用和必要的?

AnyTypeMovable   v;
AnyTypeMovable&& r = move(v);

【问题讨论】:

  • 我会说它相当没用,因为r 现在是一个左值(它有一个名字!),无论如何你需要std::move(r) 使它成为一个右值。
  • 有趣的事实:v 不需要是可移动的。
  • 有趣的事实:这段代码中没有发生任何动作。
  • 我不会说它是 100% 没用的,你可以用它来告诉接下来阅读代码的人这个变量是一个临时变量,它会被移动,尽管我认为一般来说很容易发现当std::move 被调用时
  • @DavidRodríguez-dribeas Devils Advocate

标签: c++ c++11 scope move-semantics rvalue-reference


【解决方案1】:

不,AnyTypeMovable&& r = move(v); 这里一点用处都没有。

考虑以下代码:

#include <iostream>
#include <vector>

class MyMovableType
{
        int i;
public:
        MyMovableType(int val): i(val){}
        MyMovableType(MyMovableType&& r) { this->i = r.i; r.i = -1; }
        MyMovableType(const MyMovableType& r){ this->i = r.i; }
        int getVal(){ return i; }
};

int main()
{
        std::vector<MyMovableType> vec;
        MyMovableType a(10);
        MyMovableType&& aa = std::move(a);

        vec.push_back(aa);

        std::cout << a.getVal() << std::endl;

        return 0;

}

由于aa 是一个左值(如R. Martinho FernandesXeo 所述 - 命名的右值引用是一个左值),这将打印10表示移动还没有被执行(也没有在赋值中,也没有在push_back调用中),所以你仍然需要std::move它到push_back方法,如本例:

#include <iostream>
#include <vector>

class MyMovableType
{
        int i;
public:
        MyMovableType(int val): i(val){}
        MyMovableType(MyMovableType&& r) { this->i = r.i; r.i = -1; }
        MyMovableType(const MyMovableType& r){ this->i = r.i; }
        int getVal(){ return i; }
};

int main()
{
        std::vector<MyMovableType> vec;
        MyMovableType a(10);
        MyMovableType&& aa = std::move(a);

        vec.push_back(std::move(aa));

        std::cout << a.getVal() << std::endl;

        return 0;

}

将执行移动,因此打印输出将为-1。因此,尽管您将aa 传递给push_back,您仍然需要通过std::move 传递它。

【讨论】:

  • 所以在这种情况下std::string&amp;&amp;std::string&amp; 是一样的吗?标准是这样定义的吗?
  • @Haatschii:是的,一个命名的右值引用是一个左值。
  • 第一个测试是不确定的,顺便说一句。移动 std::string 不需要将原始字符串保留在任何特定状态。它只需要有效,并且具有相同的原始值就可以了。
  • @R.MartinhoFernandes 你说得对,我会写一个简单的类来代替。
【解决方案2】:

注意,命名右值是左值。所以你应该使用 std::forward。

#include <iostream>
#include <vector>

class MyMovableType
{
        int i;
public:
        MyMovableType(int val)  noexcept   : i(val){}
        MyMovableType(MyMovableType&& r) noexcept { this->i = r.i; r.i = -1; }
        MyMovableType(const MyMovableType& r) noexcept{ this->i = r.i; }
        int getVal()const noexcept{ return i; }
};

int main()
{
        std::vector<MyMovableType> vec;
        MyMovableType a(10);
        MyMovableType&& aa = std::move(a);

        vec.push_back( std::forward<decltype(a)>(aa) );

        std::cout << a.getVal() << std::endl; // -1 printed.

        return 0;

}

【讨论】:

  • 如果我理解正确的话,这里的 std::forward 调用会和 std::move 做同样的事情(也就是说,它最终会移动前面尚未移动的 aa调用 std::move)——但这里不应使用 std::forward,因为它的目的是“转发”在 std::forward 调用范围内未知的类型信息,并且所述信息不是此处未知(即,我们知道所有关于 aa 的类型)。 (注意——我才刚刚了解这些东西,主要是在模仿 Scott Myers:scottmeyers.blogspot.com/2012/11/…
  • 是的,保留 std::forward 以进行正确的参数转发,而不是移动语义
猜你喜欢
  • 2020-02-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-11-23
  • 1970-01-01
  • 2020-08-22
  • 1970-01-01
相关资源
最近更新 更多