I'm curious about why I can't compile the following code.
It's nonsense code (I know), but I originally faced the problem in some other code using templates with perfect forwarding and such.
I managed to narrow the problem down to std::move
/ std::forward
/ std::remove_reference
, and I'm curious why it needs a temporary in the first place...
#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));
}
Compiling with Clang (3.7), it gives me the following error:
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
I understand I can't bind a temporary to a non-const
reference, and there are plenty of questions answering why that is not allowed.
I would expect the code to just carry a reference to foo
in main
up to Foo& f2
, thus not needing a temporary.
Visual Studio accepts the code, but GCC and Clang fail to compile it; although Visual Studio is not as strict of course.
An implementation for std::move () is given in N2027: "A Brief Introduction to Rvalue References" as follows: template <class T> typename remove_reference<T>::type&& std::move (T&& a) { return a; }
In particular, std::move produces an xvalue expression that identifies its argument t. It is exactly equivalent to a static_cast to an rvalue reference type.
What are we to do? In C++11, std::move is a standard library function that casts (using static_cast) its argument into an r-value reference, so that move semantics can be invoked. Thus, we can use std::move to cast an l-value into a type that will prefer being moved over being copied. std::move is defined in the utility header.
std::move () is a function used to convert an lvalue reference into the rvalue reference. Used to move the resources from a source object i.e. for efficient transfer of resources from one object to another.
Well:
Foo& f2 = std::move(t);
f2
is a reference, so where are you move
ing to? You're not actually moving at all.
std::move
returns an rvalue reference; you cannot assign this to an lvalue reference variable (consider that an rvalue reference can be a reference to a temporary). So, the compiler complains that you are assigning a reference to a temporary (because std::move
creates what the compiler considers to be a reference to a temporary, that is, an rvalue reference).
There's no actual creation of a temporary; it's just that std::move
returns an rvalue reference, and you are not allowed to assign such to an lvalue reference. (The only possible "temporary" is the one referred to by the t
parameter, which is declared as an rvalue reference; it so happens that your example passes in something that is not a temporary, via move
, but it could just as easily have passed a reference to an actual temporary).
So in short, the problem is not that it needs a temporary, but that you are assigning an rvalue reference (which potentially refers to a temporary) to an lvalue-reference variable. The Clang error message is a little misleading, because it implies the existence of a temporary, whereas an rvalue reference might not actually refer to a temporary. GCC produces this instead:
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);
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With