Does copy-and-swap still give the strong exception guarantee in C++11?

308 Views Asked by At

The copy-and-swap idiom is said to provide a strong exception guarantee. But in C++11, std::swap uses move operations.

Consider the following code:

class MyClass
{
    AClass x;
    CanThrowIfMoved throwingObject;

    MyClass(MyClass&& other) noexcept
        x(std::move(other.x)),
        throwingObject(std::move(other.throwingObject))
    { }

    friend void swap(MyClass& first, MyClass& second) noexcept
    {
        using std::swap;
        swap(first.x, other.x);
        swap(first.throwingObject, other.throwingObject);
    }

    MyClass& operator=(MyClass other) noexcept
    {
        swap(*this, other);
        return *this;
    }
};

If throwingObject throws during the swap, the strong exception guarantee is broken.

The noexcept keywords don't enforce anything during compile time. throwingObject can still throw, the only difference is that the program will violently terminate. I don't think crashing the entire application when an exception occurs counts as a strong exception guarantee.

Does this mean that copy-and-swap no longer enforces the strong exception guarantee in C++11?


Similar questions

This question is similar, but it's targeted at using the standard library. I'm interested in what this issue means for the strong exception guarantee of the copy-and-swap idiom.

This question discusses how to use noexcept in the copy-and-swap idiom, but only discusses the copy. Not the swap, which is where the problem seems to be.

1

There are 1 best solutions below

3
On BEST ANSWER

Instead of directly invoking swap in the swap member method, use a helper function template, that checks at compile time the noexcept guarantee:

friend void swap(MyClass& first, MyClass& second) noexcept
{
    util::swap_noexcept(first.x, other.x);
    util::swap_noexcept(first.throwingObject, other.throwingObject);
}

namespace util
{
    template <typename ...Args>
    void swap_noexcept(Args&&... args) noexcept
    {
        using std::swap;
        static_assert(noexcept(swap(std::forward<Args>(args)...)), "requires noexcept");
        swap(std::forward<Args>(args)...);
    }
}