声明本地变量为右值引用(rvalue-reference),例如T&& r = move(v),是否没有用处?

32

在特定情况下,你们能否给我提供一个具有说明性的例子,以证明以下陈述的有用性和必要性?

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

7
我认为这样做是相当无用的,因为r现在是一个左值(它有一个名称!),而且你需要使用std::move(r)来使它成为右值。 - R. Martinho Fernandes
4
有趣的事实:v不需要是可移动的。 - Xeo
16
有趣的事实:这段代码中没有发生任何移动。 - Griwes
2
我不会说它完全没有用,你可以使用它告诉下一个阅读代码的人这个变量是临时的,并且它将被移动,尽管我想通常情况下很容易发现何时调用std::move - aaronman
@DavidRodríguez-dribeas 魔鬼的代言人 - aaronman
2个回答

16

不,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;

}

正如R. Martinho FernandesXeo指出的那样(命名的右值引用是左值),由于aa是一个左值,因此这将打印出10,表明移动操作尚未执行(无论是在赋值还是在push_back调用中),因此您仍需要将其std::movepush_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。因此,尽管您正在通过push_back传递aa,但仍然需要通过std::move传递它。


3
在这个上下文中,std::string&& 就相当于 std::string& 吗?这是标准如何定义的? - Haatschii
4
@Haatschii:是的,命名的右值引用是左值。 - Xeo
5
第一次测试结果不确定。移动 std::string 并不要求原始字符串保持在特定的状态。它只需要是有效的,而且它的原始值保持不变也完全没有问题。 - R. Martinho Fernandes
@R.MartinhoFernandes 你说得对,我会写一个简单的类。 - Nemanja Boric

1
请注意,命名的右值引用是左值。因此,您应该使用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;

}

4
如果我理解正确,这里的 std::forward 调用将执行与 std::move 相同的操作(即最终移动之前未被 std::move 调用移动的 aa) - 然而尽管如此,不应在此处使用 std::forward,因为它的目的是“转发”在 std::forward 调用范围内未知的类型信息,而在此处我们已经知道有关 aa 类型的所有信息。(请注意 - 我刚刚学习了这些内容,主要是在引用 Scott Myers 的观点:http://scottmeyers.blogspot.com/2012/11/on-superfluousness-of-stdmove.html) - mjwach
是的,保留std::forward以进行正确的参数转发,而不是移动语义。 - Julien__

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接