0

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.

Community
  • 1
  • 1
Aberrant
  • 3,423
  • 1
  • 27
  • 38
  • 4
    The main prerequisite of copy-swap is a non-throwing `swap` function, so your problem is not with copy-swap. – chris Jun 26 '14 at 19:05
  • @chris The main sentiment behind this question is whether it is even possible to provide a non-throwing swap function, considering you rely on the move constructors of other classes (whose definitions may change in the future). – Aberrant Jun 26 '14 at 19:24

1 Answers1

2

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)...);
    }
}
nosid
  • 48,932
  • 13
  • 112
  • 139
  • It looks like this would work. Does this mean these static assertions should really be used in every swap function? The types and class definitions of the members could always change to remove their `noexcept` so it doesn't seem safe to manually check for those and expect them to stay that way. – Aberrant Jun 26 '14 at 19:22
  • @Aberrant: The helper function `swap_noexcept` has to be written only once, and so the `static_assert`. If the `noexcept` is removed from a `swap` function, it's good that the compilation fails, because otherwise your code will break at runtime. – nosid Jun 26 '14 at 19:32
  • Sorry, I think I phrased that wrong. My question is, am I right to think that you _must_ always use these static asserts in some way to really be able to give the strong guarentee? – Aberrant Jun 26 '14 at 19:42