0

I'm reading this answer about move semantic and come up with a problem. He describes the so-called move and swap idiom:

unique_ptr& operator=(unique_ptr source)   // note the missing reference
{
    std::swap(ptr, source.ptr);
    return *this;
}

Since the move assignment operator are supposed to operate on rvalue references, I thought that it's acceptable to pass the rvalue reference as an argument. Here is an example:

#include <iostream>

struct A {
    A(){ }
    virtual ~A(){ }
    A(A&&){ }
};

void foo(A a){
    std::cout << "foo(A)" << std::endl;
}

int main()
{
    foo(*new A);
}

DEMO

But the compiler warns me that it tried to copy the referenced object and failed because the copy-constructor is deleted. I don't udnerstand why it's correct in the example with unique_ptr. The object is supposed to be copied when we invoke the function, so there's no point of move semantic.

Couldn't you explain it a bit?

Community
  • 1
  • 1
stella
  • 2,546
  • 2
  • 18
  • 33
  • 4
    I'm not sure how strong your grasp of c++ is, the `*new T` is usually a giveaway of not understanding value semantics. What you would want in real life is `foo(A{});` but it's of course possible you know that and it's just academic. – Ryan Haining Dec 17 '15 at 04:26
  • 1
    `*new A` is an lvalue. It designates a memory location where an object is stored. To move you'd have to write `std::move(*new A)` (which would leak memory) – M.M Dec 17 '15 at 04:39
  • "`*new A`", how are you going to `delete` that `A`? – Emil Laine Dec 17 '15 at 07:01

2 Answers2

4

The expression *(new A) is not an rvalue.

The general rule (not the actual rule, just a quickie) for what expressions are rvalues is this: it's either a temporary or something you explicitly called std::move on. And *(new A) is not a temporary. And you didn't call move on it. So it's not an rvalue.

Try it with A() instead.

Community
  • 1
  • 1
Nicol Bolas
  • 449,505
  • 63
  • 781
  • 982
2

The move constructor is invoked to construct a new object from an rvalue of the same type. If you try to pass an lvalue to a function that takes a move-only type by value, it needs the copy constructor and you'll get an error.

void f(std::unique_ptr<Foo> p);
std::unique_ptr<Foo> p;
f(p);  // error
f(std::move(p));  // OK

In your example, it is possible to do this:

foo(std::move(*new A));

but *new A alone is not a possible argument, since dereferencing a pointer always produces an lvalue.

Brian Bi
  • 111,498
  • 10
  • 176
  • 312