我很好奇为什么我无法编译以下代码。
这是无意义的代码(我知道),但我最初在使用具有完美转发等模板的其他代码中遇到了这个问题。
我设法将问题缩小到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
中引用main
到Foo& f2
,因此不需要临时。
Visual Studio接受代码,但GCC和Clang无法编译它;虽然Visual Studio当然不那么严格。
答案 0 :(得分:9)
我希望代码只能在
foo
中引用main
到Foo& f2
,因此不需要临时。
它不像推进参考那么简单。这里的std::move
是一种很好的说法,“将这个左值化,以便我可以将它用作右值”。这实际上是为什么它不起作用。
特别是,
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 ++中是禁止的