Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

std::move(T&&) and temporary object. Where is the temporary coming from?

Tags:

c++

c++11

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.

like image 852
RuiFig Avatar asked Apr 08 '16 10:04

RuiFig


People also ask

Is there an implementation for std::move()?

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; }

What does move () do in C++?

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 is move in C++11?

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.

What is the use of MOVE () function in Python?

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.


1 Answers

Well:

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

f2 is a reference, so where are you moveing 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);
like image 63
davmac Avatar answered Sep 28 '22 09:09

davmac