Welcome back.

As promised, more content from the newly completed Implementing QuantLib. This week, a class with its feet firmly planted in the past.

In other news, there are still places available for my next course. Click the link, I’ll wait.

Done? Great. Here we go.

Subscribe to my Substack to receive my posts in your inbox, or follow me on Twitter or LinkedIn if you want to be notified of new posts, or subscribe via RSS if you’re the tech type: the buttons for all that are in the footer. Also, I’m available for training, both online and (when possible) on-site: visit my Training page for more information.

Disposable objects

The Disposable class template was an attempt to implement move semantics in C++98 code. To give credit where it’s due, we took the idea and technique from an article by Andrei Alexandrescu [1] in which he described how to avoid copies when returning temporaries.

The basic idea is the one that was starting to float around in those years and that was given its final form in C++11: when passing a temporary object, copying it into another one is often less efficient than swapping its contents with those of the target. You want to move a temporary vector? Copy into the new object the pointer to its storage, instead of allocating a new one and copying the elements. In modern C++, the language itself supports move semantics with the concept of rvalue reference [2]; the compiler knows when it’s dealing with a temporary, and we can use std::move in the few cases when we want to turn an object into one. In our implementation, shown in the following listing, we don’t have such support; you’ll see the consequences of this in a minute.

    template <class T>
    class Disposable : public T {
      public:
        Disposable(T& t) {
            this->swap(t);
        }
        Disposable(const Disposable<T>& t) : T() {
            this->swap(const_cast<Disposable<T>&>(t));
        }
        Disposable<T>& operator=(const Disposable<T>& t) {
            this->swap(const_cast<Disposable<T>&>(t));
            return *this;
        }
    };

The class itself is not much to look at. It relies on the template argument implementing a swap method; this is where any resource contained inside the class are swapped (hopefully in a cheap way) instead of copied. The constructors and the assignment operator all use this to move stuff around without copies—with a difference, depending on what is passed. When building a Disposable from another one, we take it by const reference because we want the argument to bind to temporaries; that’s what most disposables will be. This forces us to use a const_cast in the body, when it’s time to call swap and take the resources from the disposable. When building a Disposable from a non-disposable object, instead, we take is as a non-const reference; this is to prevent ourselves from triggering unwanted destructive conversions and from finding ourselves with the empty husk of an object when we thought to have a usable one. This, however, has a disadvantage; I’ll get to it in a minute.

The next listing shows how to retrofit Disposable to a class; Array, in this case.

    Array::Array(const Disposable<Array>& from)
    : data_((Real*)(0)), n_(0) {
        swap(const_cast<Disposable<Array>&>(from));
    }

    Array& Array::operator=(const Disposable<Array>& from) {
        swap(const_cast<Disposable<Array>&>(from));
        return *this;
    }

    void Array::swap(Array& from) {
        data_.swap(from.data_);
        std::swap(n_,from.n_);
    }

As you see, we need to add a constructor and an assignment operator taking a Disposable (in C++11, they would be a move constructor and a move assignment operators taking an rvalue reference) as well as the swap method that will be used in all of them. Again, the constructors take the Disposable by const reference and cast it later, in order to bind to temporaries—although now that I think of it, they could take it by copy, adding another cheap swap.

Finally, the way Disposable is used is by returning it from function, like in the following code:

    Disposable<Array> ones(Size n) {
        Array result(n, 1.0);
        return result;
    }

    Array a = ones(10);

Returning the array causes it to be converted to Disposable, and assigning the returned object causes its contents to be swapped into a.

Now, you might remember that I talked about a disadvantage when I showed you the Disposable constructor being safe and taking an object by non-const reference. It’s that it can’t bind to temporaries; therefore, the function above can’t be written more simply as:

    Disposable<Array> ones(Size n) {
        return Array(n, 1.0);
    }

because that wouldn’t compile. This forces us to take the more verbose route and give the array a name. (Well, it doesn’t actually force us, but writing return Disposable<Array>(Array(n, 10)) is even uglier than the alternative.)

Nowadays, of course, we’d use rvalue references and move constructors and forget all about the above. To tell the truth, I’ve a nagging suspicion that Disposable might be getting in the way of the compiler and doing more harm than good. Do you know the best way to write code like the above and avoid abstraction penalty in modern C++? It’s this one:

    Array ones(Size n) {
        return Array(n, 1.0);
    }

    Array a = ones(10);

In C++17, the copies that might have been done when returning the array and when assigning it are guaranteed to be elided (that is, the compiler will generate code that builds the returned array directly inside the one we’re assigning); most recent compilers have been doing that for a while, without waiting for the standard to bind them. It’s called RVO, for Return Value Optimization, and using Disposable prevents it and thus might make the code slower instead of faster.

Bibliography

[1] A. Alexandrescu, Move Constructors. In C/C++ Users Journal, February 2003.

[2] H.E. Hinnant, B. Stroustrup and B. Kozicki, A Brief Introduction to Rvalue References. C++ Standards Committee Paper N2027, 2006.