Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there a point to define move-only objects in c++11?

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)?
like image 487
Govan Avatar asked Oct 24 '15 10:10

Govan


2 Answers

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.

like image 88
tweej Avatar answered Nov 08 '22 14:11

tweej


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.

like image 36
David Haim Avatar answered Nov 08 '22 14:11

David Haim