10

I had a question about using unique-ptrs before. I get this answer recommending to use move-only objects. I defined a class as below:

class B {
    const string objName;
public:

    B ( B && ) = default;
    B &  operator= ( B && ) = default;
    B ( const B & ) = delete;
    B & operator= ( const B & ) = delete;

    B(const string & name) :
            objName(name) {

    }

    virtual ~B();

    const string name() const { return objName;};
}

and I called B by this lines:

class A {
A(){}
void take(B b);
}

A a; 
B b("test");
cout<<b.name();
a.take(std::move(b));
cout<<b.name();

My questions:

  1. Even if I have defaulted the move constructor, I can not write a.take(b) and I am getting compiling error. I understand that copy constrctor is deleted but It seems that the logical choice is to use move constructor when it is defaulted without need for writing std::move like this a.take(b)
  2. In the result the "test" is printed twice. Why the b object has not been removed after calling move? If the b object still exists and a copy of it has been sent to the a.take(move(b)) then it means we don't have any use of move for rvalue objects.
  3. Is it a good practice to use move-only objects as above (Removing copy constructor and assignment operator and defaulting move constructor and move assignment)?
Community
  • 1
  • 1
Govan
  • 2,079
  • 4
  • 31
  • 53
  • 1
    Try replacing "test" with a longer string and see what happens. In general the value of the moved-from object is indeterminate; it could be the old value or not. – Alan Stokes Oct 24 '15 at 10:21
  • 4
    You need to remove the `const` from `objName` if you want to move from it. – Alan Stokes Oct 24 '15 at 10:22
  • @AlanStokes removing const will cause that test wil be printed once. Is That mean that the const memebers will never be moved? Or they will be copied? – Govan Oct 24 '15 at 10:46
  • 1
    They can't be moved from, so they have to be copied. – Alan Stokes Oct 24 '15 at 10:56
  • The canonical example of a move-only object is `std::unique_ptr`, which is pretty useful. – vsoftco Oct 24 '15 at 19:55

2 Answers2

7

Yes, there is a point. Objects which manage resources (perhaps physical ones) that cannot/should not be shared between objects is the first example that comes to mind.

1) You wrote it incorrectly. Here is what I think you want based on this question and your previous one.

class B {
    std::string objName;
public:
    B ( B && ) = default;
    B &  operator= ( B && ) = default;
    B ( const B & ) = delete;
    B & operator= ( const B & ) = delete;
    B(const std::string & name) :
            objName(name) {}
    virtual ~B() {}
    std::string name() const { return objName;}
};

class A {
public:
   std::vector<B> v;
   void take(B && b)
   {
      v.push_back(std::move(b));
   }
};

int main()
{
   A a;
   B b("test");

   std::cout << "Before: " << b.name() << std::endl;
   a.take(std::move(b));
   std::cout << "After: " << b.name() << std::endl;

   std::cout << "A has elements: " << std::endl;
   for(B &b : a.v)
      std::cout << "  " << b.name() << std::endl;
}

2) You're accessing a value that has been moved-from, which doesn't make any sense! This answer already does a good job of explaining, but below I've also included text from a STL reference for std::move.

http://en.cppreference.com/w/cpp/utility/move

Unless otherwise specified, all standard library objects that have been moved from are placed in a valid but unspecified state. That is, only the functions without preconditions, such as the assignment operator, can be safely used on the object after it was moved from.

3) I have found two legitimate uses in my experience. In both cases the move-only objects controlled a physical resource which if shared by two objects would break everything.

463035818_is_not_an_ai
  • 109,796
  • 11
  • 89
  • 185
tweej
  • 832
  • 4
  • 16
  • 3
    Note that there is no need to explicitly delete the copy ctor and assignment operator. User-defined move syntax disables copy, see e.g. the live example [here](http://coliru.stacked-crooked.com/a/d20de6c4f219a22e). – vsoftco Oct 24 '15 at 19:59
  • @tweej why I should use move-only objetcts instead of unique-ptr? – Govan Oct 25 '15 at 04:38
  • @Govan: Actually, for a resource that cannot be shared, `unique_ptr` is a building brick and your move-only object wrap it up to present a pleasant interface. If it cannot be shared, how would you even write a copy constructor? – Matthieu M. Oct 25 '15 at 15:47
  • @Govan Good question, and I'm also interested in hearing what other people think about this. All of the reasons I can think of deal with performance and determinism, but are not likely to matter much in non-performance critical code. Here's one: If your vector of B's in class A was instead a vector of unique_ptrs to B, each access to B would require dereferencing a pointer, and you've lost the benefits of hardware-assisted prefetching as you loop through the elements of the vector. I will say that I've used unique_ptr and shared_ptr much more often than creating my own move-only types. – tweej Oct 25 '15 at 23:10
3

About 3 : of course there is. I can think about many examples of objects that can be moved and not be copied, or shoudln't, anyway.

One example is a Socket class:
1) you want to give a default constructor for "empty" Socket which still didn't receive any IP or port.
2) you want give a constructor which gets an IP and port. upon construction the Socket will try to connect and may throw an exception if the connection fails
3) obvious thing is the destructor - it will disconnect the object and release any underlying-system resources that this object may hold

what about move constructor vs copy constructor?
let's say I have a function wich creates a socket and returns it. If I call the copy constructor that means that:
the new copied socket(return value) will try to connect to the same IP and port that the source socket was connected to. that may not be possible at all.
the source socket will be disconnected and destructed

it is quite possible that trying to copy the socket will just create a huge mess. on the contrary, a move constructor solves this mass beutifully:
the return value receive all the underlying OS resources without disconnecting them or destroying them.
the source socket stays empty and the destructor has nothing to disconnect or destroy.

it appears that sockets would most likely to be only moved, not copied.

this could apply to a "Process" class - If I'll try to return a process by copying, I'd might try to open the same process again, only to shut down the original. a mess! by moving the process around I don't do it. I simply move the process around from function to function.

David Haim
  • 25,446
  • 3
  • 44
  • 78
  • Thanks for your answer. I understand the need for move and differences between mover and copy. What I am wondering is need for move for rvalue objects. You can implement socket by the unique_ptr. Question is differences between move(unique_ptr(new Socket())) or move(Socket()) – Govan Oct 25 '15 at 04:32