Do I understand RAII in conjunction with the copy/swap idiom correctly?

302 views Asked by At
class Resource {
    Handle resource_handle;
public:
    friend void swap(Resource &a, Resource &b); // swap for the partial copy/swap idiom

    Resource(); // Default with uninitialized handle whose destruction is a noop
    Resource(std::string location); // Construction of resource (e.g. load something from disk)
    Resource(Resource &&other); // Move constructor to receive from returns of functions
    Resource &operator=(Resource other); // Sawp assignment to implement copy/swap idiom
    Resoruce(Resource &other) = delete; // You can not copy resources
    Resource &operator=(Resource &other) = delete; // You can not copy resources
};

A class managing a handle to a resource (file handles, gpu handles, mutexes) wants to prevent that the handle of the resoruce ever gets copied so the deconstruction of the wrapper class automatically frees the resource once and only once and nothing can access the handle anymore because the lifetime of the object has ended and (hopefully) no reference or pointer to the wrapper exists anymore.

The copy/swap and rule of 5(and a half) says that usually you want to define a copy constructor / assignment operator. Copying a resource handle is explicitly unwanted. Do I understand correctly that thus just deleting any other constructor / assignment operator solves this problem (and the compiler will shout at me if I ever assign something that is not converted to a rvalue (that therefore doesn't exist anymore after the assignment is done))

This is related to this question, as the resources I want to construct are actually only constructible after the containing data structure they are a member of is already constructed, making it necessary to move resources, but not copy them.

Parallel resource loading for OpenGL

1

There are 1 answers

4
Andrei Matveiakin On

Deleting copy constructor and copy assignment operator for a resource handle class makes perfect sense and it would produce the desired result. (Note that copy constructor and copy assignment typically take a const reference argument. It doesn't matter here because the operator is deleted, but personally I always stick to a const reference unless there is a reason to do otherwise. I find that it makes the code easier to read.)

However the “swap assignment” is problematic.

First, it wouldn't work the way it's written in your example:

Resource &operator=(Resource other);  // bad

This functions takes an argument by value thus creating a copy. This wouldn't compile because copy constructor is deleted.

Second, even if something like this did work it would be misleading. People usually expect the right-hand side of an assignment to remain unchanged. I suggest to replace the “swap assignment” with a swap method:

void swap(Resource& other); 

And then you can use this method to implement a non-member swap function, and it doesn't even need to be a friend function if the swap method is public.