2

I like the new std::move but afraid that it reduces my program maintainability.

To my knowledge, if I create move constructor or move assignment operator=(), I have to write it from scratch. That is where the problem begins.

Code version 1

Here is a tiny class:-

class B{
    M shouldBeMove;  //if it is copied, it is still correct (but prefer move)
    C shouldBeCopy;  //can be copied or moved, both are equal and ok
    //wow, I don't even have to write this line for operator=():-
    //    this->shouldBeCopy = that.shouldBeCopy
}

B b1;
B b2=b1;

Currently, B b2=b1 will copy both M and C. It is ok.

Code version 2

Now I want to use the power of std::move :-

class B{
    M shouldBeMove;  //now, the program is refactored that it must be moved 
    // M now has "M& operator=(M&& that)"
    C shouldBeCopy;
    B& operator=(B&& that){
        this->shouldBeMove=std::move(that.shouldBeMove);
        this->shouldBeCopy=that.shouldBeCopy;   //<-- a bit tedious (1#)
        //  ... imagine that there are 10 variables to be copied ... 
    }
}

B b1;
B b2=std::move(b1);

It is still ok, but a bit tedious. (1#)

Code version 3

Then one month in the future, I may want to add a new field e.g. C shouldBeCopy2 to B, I also have to add a line into operator= :-

B& operator=(B&& that){
    this->shouldBeMove=std::move(that.shouldBeMove);
    this->shouldBeCopy=that.shouldBeCopy;
    this->shouldBeCopy2=that.shouldBeCopy2;  //<--- new line
}

I think I am a type that may forget to add that line. (2#)

Question:

1#. How to make it not tedious?
2#. How to foolproof my mistake?

javaLover
  • 6,347
  • 2
  • 22
  • 67
  • 1
    Use [rule of zero](http://en.cppreference.com/w/cpp/language/rule_of_three) – Bryan Chen Nov 17 '16 at 04:35
  • What would go wrong if a member of type `C` were moved? Why do you have this weird restriction where certain members need to be copied in the move constructor? – user2357112 Nov 17 '16 at 04:47
  • @Bryan Chen I tried to follow your link, and think it is not a solution. .... Case 1: `B` is a resource manager itself e.g. array , and `C` is a management setting like `size of array` or `storage mode` , I still have to copy `C` myself. .... Case 2: `B` is a complex AI, `M` is a complex database, I still have to copy `C` (e.g. AI setting) myself too. – javaLover Nov 17 '16 at 04:48
  • @user2357112 Good point. Nothing will go wrong if `C` is moved. I will edit my question. Thank. – javaLover Nov 17 '16 at 04:48
  • So... why not just use the default move constructor and move assignment operator? And if both the `C` and `M` members are okay to move, is there any difference in the requirements surrounding them? – user2357112 Nov 17 '16 at 04:51
  • @user2357112 What? The default move constructor and move assignment will `std::move` every field? I don't know about it! I just think the `move` ones will do same as default (not move) copy constructor/assignment do. So what you mean is that I don't even need to create `B& operator=(B&& that)` in the first place, right? – javaLover Nov 17 '16 at 04:53
  • @user2357112 I don't think there are default move constructor and move assignment operator. http://stackoverflow.com/questions/4819936/why-no-default-move-assignment-move-constructor :( – javaLover Nov 17 '16 at 05:01
  • 1
    @javaLover: There are. That questioner was wrong. – user2357112 Nov 17 '16 at 05:19
  • @user2357112 If I don't code that `operator==(B&&)`, I will receive error "deleted function 'B::B(B&&)'" , https://ideone.com/7NrAQ7 . – javaLover Nov 17 '16 at 05:28
  • @javaLover: The error is telling you that `B` has an implicitly deleted move *constructor*, because `B b2=std::move(b);` actually tries to construct `b2` from `std::move(b)` instead of assigning it. Defining the move assignment operator for `M` turned off the default move constructor for `M`, which in turn means `B` has no move constructor. – user2357112 Nov 17 '16 at 05:30
  • @user2357112 I want to define custom move for `M`, so I have to define custom move for `B`, right? Thus, I can't use default move for `B`. : ( – javaLover Nov 17 '16 at 05:34
  • @javaLover: A custom move constructor for `M` won't turn off the default move constructor for `B`. The problem was that `M` didn't have any move constructor, default or user-defined. – user2357112 Nov 17 '16 at 05:35
  • I still don't understand your question. There is no "non moveable" type in C++ because it should always fallback to use copy for type that doesn't support move (e.g. int). So when all of your classes are moveable and the default move operator/constructor should take care of everything. – Bryan Chen Nov 17 '16 at 05:40
  • @Bryan Chen I think you are correct, but I don't know how to activate the default move operator/constructor of `B` (to make it really move, not just copy). To do so, I am still trying to follow what user2357112 said. – javaLover Nov 17 '16 at 05:43

1 Answers1

1

You should follow rule of zero and let compiler generate the constructors and assign operators for you.

But when you need to implement a moveable type, make sure you implement both move assignment operator (T& operator=(T&&)) and move constructor (T(T&&)). Please follow rule of five and ensure the class have proper copy constructor/move constructor/copy assignment operator/move assignment operator/destructor

https://ideone.com/UVZNOM

#include <iostream>
using namespace std;

class M{
    public: int database=0;
    M& operator=(M&& other){
        this->database=other.database;
        other.database=0;
        return *this;

    }
    M(M &&other) {
        *this = std::move(other);
    }
    M (M& m)=default;
    M ()=default;
    ~M() { /* free db */ }
};
class B{ // As rule of zero, you don't need to implement constructors and assignment operators 
    public: M shouldMove;
};

int main() {
    B b;
    b.shouldMove.database=5;
    B b2=std::move(b);
    std::cout<< b.shouldMove.database <<std::endl;
    std::cout<< b2.shouldMove.database <<std::endl;
    return 0;
}
Bryan Chen
  • 45,816
  • 18
  • 112
  • 143
  • You should be more clear about `M`: what properties of `M` justify writing `&&` assign/ctor manually? I would write `~M` that somehow uses the `database` value to (in theory) destroy the database (if non-zero); in short, either mention or describe the rule-of-five (the complement to the rule-of-zero) – Yakk - Adam Nevraumont Nov 17 '16 at 14:08