c++c++11copy-constructorassignment-operatorcopy-and-swap

What is the Rule of Four (and a half)?


For properly handling object copying, the rule of thumb is the Rule of Three. With C++11, move semantics are a thing, so instead it's the Rule of Five. However, in discussions around here and on the internet, I've also seen references to the Rule of Four (and a half), which is a combination of the Rule of Five and the copy-and-swap idiom.

So what exactly is the Rule of Four (and a half)? Which functions need to be implemented, and what should each function's body look like? Which function is the half? Are there any disadvantages or warnings for this approach, compared to the Rule of Five?

Here's a reference implementation that resembles my current code. If this is incorrect, what would a correct implementation look like?

#include <utility>

// "Handle management" functions. These would be defined in an external library.
typedef int* handle;
#define NO_HANDLE nullptr
extern handle get_handle(int value);
extern handle copy_handle(handle h);
extern void free_handle(handle h);

// This class automatically obtains a handle, and then frees it when the object
// leaves scope.
class AutoHandle {
public:
    //We must have a default constructor so we can swap during copy construction.
    //It need not be useful, but it should be swappable and deconstructable.
    //It can be private, if it's not truly a valid state for the object.
    AutoHandle() : resource(NO_HANDLE) {}
    
    //Normal constructor, acquire resource
    AutoHandle(int value) : resource(get_handle(value)) {}
    
    //Copy constructor
    AutoHandle(AutoHandle const& other) {
        resource = copy_handle(other.resource);
    }
    
    //Move constructor
    //Delegates to default constructor to put us in safe state.
    AutoHandle(AutoHandle&& other) : AutoHandle() {
        swap(other);
    }
    
    //Assignment
    AutoHandle& operator=(AutoHandle other) {
        swap(other);
        return *this;
    }
    
    //Destructor
    ~AutoHandle() {
        //Free the resource here.
        //We must handle the default state that can appear from the copy ctor.
        if (resource != NO_HANDLE)
            free_handle(resource);
    }
    
    //Swap
    void swap(AutoHandle& other) {
        using std::swap;
        
        //Swap the resource between instances here.
        swap(resource, other.resource);
    }
    
    //Swap for ADL
    friend void swap(AutoHandle& left, AutoHandle& right) {
        left.swap(right);
    }
    
private:
    handle resource;
};

Solution

  • So what exactly is the Rule of Four (and a half)?

    “The Rule of The Big Four (and a half)" states that if you implement one of

    then you must have a policy about the others.

    Which functions need to implemented, and what should each function's body look like?

    Which function is the half?

    From previous article:

    "To implement the Copy-Swap idiom your resource management class must also implement a swap() function to perform a member-by-member swap (there’s your “…(and a half)”)"

    so the swap method.

    Are there any disadvantages or warnings for this approach, compared to the Rule of Five?

    The warning I already wrote is about to write the correct swap to avoid the infinite recursion.