std :: move(T&&)和临时对象。临时来自何处?

时间:2016-04-08 10:38:15

标签: c++ c++11

我很好奇为什么我无法编译以下代码。

这是无意义的代码(我知道),但我最初在使用具有完美转发等模板的其他代码中遇到了这个问题。

我设法将问题缩小到std::move / std::forward / std::remove_reference,我很好奇为什么它首先需要暂时的......

#include <utility>
#include <stdio.h>

struct Foo {
    Foo(Foo&& other) {
        printf("Foo::(Foo&&) %p\n", this);
    }
    Foo() {
        printf("Foo::() %p\n", this);
    }
    ~ Foo() {
        printf("Foo::~Foo() %p\n", this);
    }
};

void test(Foo&& t)
{
    // OK: Works fine and "f1" is is constructed with Foo::Foo(Foo&& other)
    // Foo f1 = std::move(t);

    // ERROR: Here it is trying to  bind a temporary Foo to a non-const lvalue
    // I can't figure out why it needs to create a temporary.
    Foo& f2 = std::move(t);
}

int main()
{
    Foo foo;
    test(std::move(foo));
}

使用Clang(3.7)进行编译,它给出了以下错误:

23 : error: non-const lvalue reference to type 'Foo' cannot bind to a temporary of type 'typename std::remove_reference<Foo &>::type' (aka 'Foo')
Foo& f2 = std::move(t);
^ ~~~~~~~~~~~~
1 error generated.
Compilation failed

我知道我不能将临时绑定到非const引用,并且有很多问题可以回答为什么不允许这样做。

我希望代码只能在foo中引用mainFoo& f2,因此不需要临时。

Visual Studio接受代码,但GCC和Clang无法编译它;虽然Visual Studio当然不那么严格。

5 个答案:

答案 0 :(得分:9)

  

我希望代码只能在foo中引用mainFoo& f2,因此不需要临时。

它不像推进参考那么简单。这里的std::move是一种很好的说法,“将这个左值化,以便我可以将它用作右值”。这实际上是为什么它不起作用。

来自the cppreference;

  

特别是,std::move生成一个xvalue表达式,用于标识其参数t。它完全等同于static_cast到右值引用类型。

Foo& f2需要左值来绑定引用,您提供右值引用 - 因此出错。

没有临时创建,在这方面错误消息具有误导性。您正在投射引用以允许进行value category次转换。完成这些转换后,可以通过适当的移动构造函数或赋值运算符执行移动。

作为旁注:VS可能允许这样做,因为它有一个非标准扩展,允许rvalues绑定到非const左值引用(但它会以更高的警告级别警告你/W4

答案 1 :(得分:8)

好:

Foo& f2 = std::move(t);

f2是一个参考,那你在哪里move?你实际上根本就没有移动。

std::move返回右值引用;你不能将它赋给左值引用变量(考虑一个右值引用可以是对临时值的引用)。因此,编译器抱怨您正在分配对临时的引用(因为std::move创建了编译器认为是对临时引用的引用,即右值引用)。 / p>

暂时没有实际的创作;只是std::move返回一个右值引用,并且不允许将它赋给左值引用。 (唯一可能的“临时”是t参数引用的参数,它被声明为右值引用;因此,您的示例会传递不是临时的内容,通过move,但它可以很容易地传递对实际临时的引用。)

所以简而言之,问题不在于它需要一个临时的,而是你正在为一个左值分配一个右值引用(可能指的是一个临时的) - 参考变量。 Clang错误消息有点误导,因为它意味着存在临时,而右值引用可能实际上不是指临时引用。 GCC代之以:

test2.cc: In function 'void test(Foo&&)': test2.cc:23:24: error:
invalid initialization of non-const reference of type 'Foo&' from an
rvalue of type 'std::remove_reference<Foo&>::type {aka Foo}'
     Foo& f2 = std::move(t);

答案 2 :(得分:3)

您的代码中没有临时值。

Foo& f2 = std::move(t);失败,因为左值引用无法绑定到xvalue。

要解决此问题,您可以撰写Foo& f2 = t;

clang错误消息是伪造的,也许他们使用相同的错误消息将所有尝试绑定非值左值引用到rvalue(xvalue是rvalue)并且没有为这种情况做出不同的错误消息这是相对罕见的。

答案 3 :(得分:-1)

移动对象时,需要将其移动到实际位置。在f1的情况下,您已经创建了类foo的实例,并且正在将t移动到那里。在f2的情况下,您尚未指定将t移动到的位置,因此您隐含地告诉编译器将t移动到某个临时位置,然后返回对该位置的引用。编译器不允许这样做。

答案 4 :(得分:-1)

rvalue基本上是某个表达式返回的临时对象。它被使用后立即销毁。对删除的对象进行引用/指针是没有意义的,因此在c ++中是禁止的