4

The general guideline when writing classes (using the copy-and-swap idiom) is to provide a non throwing swap member function. (Effective C++, 3rd edition, Item 25 and other resources)

However, what if I cannot provide the nothrow guarantee because my class uses a 3rd party class member that doesn't provide a swap operation?

// Warning: Toy code !!!

class NumberBuffer {
public:
    ...
    void swap(NumberBuffer& rhs);

public:
    float* m_data;
    size_t m_n;
    CString m_desc;
};

void swap(NumberBuffer& lhs, NumberBuffer& rhs) {
    lhs.swap(rhs);
}

void NumberBuffer::swap(NumberBuffer& rhs) {
    using std::swap;
    swap(m_data, rhs.m_data);
    swap(m_n, rhs.m_n);
    swap(m_desc, rhs.m_desc); // could throw if CString IsLocked and out-of-mem
}

CString swap cannot be made no-throw, so there's the off chance the swap could fail.

Note: For rare 3rd party classes, using a smart ptr (pimpl) would be an option, but --

Note: CString is a good example as noone in his right mind (?) would start holding all members of a conceptually simple and ubiquitous class like CString via pimpl (smart ptr) because that would really look horrible -- and on the other hand, there's no (short to mid-term) chance to get the CString modified to allow fully no-throw swap.

So, is it OK to have a potentially throwing swap member function if you can't help it? (Or do you know ways around this conundrum?)

Edit: And: Can a throwing swap member be used with the copy-and-swap idiom to provide the basic guarantee if not the strong guarantee?

Martin Ba
  • 37,187
  • 33
  • 183
  • 337
  • 6
    Not an answer, but if you can move away from `CString` then the problem would be gone... – David Rodríguez - dribeas Oct 05 '11 at 12:46
  • Be aware that on a similar subject, some fairly small number of years down the line you are going to have the same problem when you try to implement C++11 move for your class. If the member can't be nothrow-swapped, then chances are it isn't going to be nothrow-movable either, because the two are very similar. – Steve Jessop Oct 05 '11 at 14:07

3 Answers3

3

So, is it OK to have a potentially throwing swap member function if you can't help it? (Or do you know ways around this conundrum?)

There is nothing inherently wrong with having a swap function that can potentially throw, but beware that without the strong exception guarantee in swap, it cannot possibly be used to provide exception safety, that is, it can only be used as swap (that is, forget about the copy-and-swap idiom for that particular class as a way of providing the strong exception guarantee... but you can still use it to reduce the amount of code --and document that it is not exception safe)

Alternatively, you can move the CString into a smart pointer that offers a no-throw swap (or at the very least the strong exception guarantee), not a nice solution, but it will at least be exception safe. Lastly, you can move away from CString altogether by using any other string library that provides whatever you need and offers a no-throw swap operation.

David Rodríguez - dribeas
  • 204,818
  • 23
  • 294
  • 489
  • +1 Can you draw a little connection from exception-safety to thread-safety? Or is this a typo? – Christian Rau Oct 05 '11 at 12:56
  • @ChristianRau: likely typo given the context... in fact it is so obvious that I subconsciously read *exception* safety :) – Matthieu M. Oct 05 '11 at 13:01
  • @ChristianRau: it was a typo, sorry for that. I am a bit tired :) – David Rodríguez - dribeas Oct 05 '11 at 13:04
  • David - yeah, no strong guarantee and as far as I can see, *also* no basic guarantee (?) unless you take a lot of care. What do you think? Could this swap be used in the copy-and-swap idiom at all? Or would one have to add some try-catch-revert stuff to provide the basic guarantee? (Maybe you could edit your answer to include this.) – Martin Ba Oct 05 '11 at 13:18
  • 1
    @Martin: you are right in that this `swap` cannot be used to provide *any* exception guarantee. The basic guarantee could be implemented by wrapping inside a try/catch, but that is basically the same with any random piece of code... – David Rodríguez - dribeas Oct 05 '11 at 13:30
  • I think you're wrong - the copy and swap idiom provides the strong exception guarantee if `swap` provides the strong exception guarantee or better. – JoeG Oct 05 '11 at 13:37
  • @JoeGauterin: How does that contradict what I said? (oh, right, the `swap` does not need to be no-throw, only provide the strong exception guarantee) – David Rodríguez - dribeas Oct 05 '11 at 13:39
  • @DavidRodríguez: Yes, you said that "without the no throw guarantee that swap cannot possibly be used to provide exception safety". – JoeG Oct 05 '11 at 13:41
2

There's nothing inherently wrong with a throwing swap, it is just less useful than a no-throw version.

The copy and swap idiom doesn't need swap to be no-throw in order to provide the strong exception guarantee. swap needs only to provide the strong exception guarantee.

The difficulty is that if the no-throw guarantee cannot be provided, it is also likely that the strong exception guarantee cannot be provided. Naive swapping using a temporary and three copies only provides the basic guarantee unless the copying operation provides the no-throw guarantee, in which case swap is also no-throw.

JoeG
  • 12,994
  • 1
  • 38
  • 63
  • Excellent points wrt. no-throw vs. strong. If there's only one member that doesn't provide the no-throw guarantee, then it's just a matter of swapping (copying) that first to get the strong-guarantee. If there's more than one member, I think one's out of luck. – Martin Ba Oct 05 '11 at 13:40
-2

You can easily make it nothrow:

void NumberBuffer::swap(NumberBuffer& rhs) throw()
{
    try
    {
        std::swap(m_desc, rhs.m_desc);   //could throw
        std::swap(m_data, rhs.m_data);
        std::swap(m_n, rhs.m_n);
    }
    catch(...)
    {
    }
}

Of course, this is no real solution to the problem, but now you at least got your non-throwing swap ;)

Christian Rau
  • 45,360
  • 10
  • 108
  • 185
  • 1
    Except that that `swap` doesn't fulfill it's contract and *silently* fails! Happy time finding those errors! I suggest you either qualify your example as how to *not* do it or delete the answer. Thanks! – Martin Ba Oct 05 '11 at 13:14
  • @Martin It wasn't meant too serious and I realize a comment would have been better for this, but feel free to down-vote. – Christian Rau Oct 05 '11 at 13:17
  • 1
    Given the `throw()` exception specification, this would be better without the try/catch. That way the compiler will arrange for `std::terminate` to be called if it throws, at least provided you're not in some non-compliant mode of MSVC. Still might not be what the caller wants, but at least it's obvious something is wrong. – Steve Jessop Oct 05 '11 at 14:03
  • "_It wasn't meant too serious_" It was pretty obvious IMO. Is there a "funny" button? (as in /.) – curiousguy Oct 06 '11 at 20:18