The reason why we can not write an exception safe copy assignment for class Widget, is because (suppose we assign t1_, then assign t2_) if T1::operator=() does not throw exception, and T2::operator=() throws exception, then since all operations on T1 may throw, it means we can not find any operations on T1 which could rollback the original status to T1 without exception thrown (if exception is thrown during rollback, means rollback is not successful, and T1 will be in an in-consistent status which is different from original status -- which violates the strong exception safe guideline).

Assume that any T1 or T2 operation might throw. Without changing the structure of the class, is it possible to write a strongly exception-safe Widget::operator=( const Widget& )? Why or why not? Draw conclusions.

Short answer: In general, no, it can't be done without changing the structure of Widget.

In the Example 3 case, it's not possible to write a strongly exception-safe Widget::operator=() because there's no way that we can change the state of both of the t1_ and t2_ members atomically. Say that we attempt to change t1_, then attempt to change t2_. The problem is twofold:

1. If the attempt to change t1_ throws, t1_ must be unchanged. That is, to make Widget::operator=() strongly exception-safe relies fundamentally on the exception safety guarantees provided by T1, namely that T1::operator=() (or whatever mutating function we are using) either succeeds or does not change its target. This comes close to requiring the strong guarantee of T1::operator=(). (The same reasoning applies to T2::operator=().)

2. If the attempt to change t1_ succeeds, but the attempt to change t2_ throws, we've entered a "halfway" state and cannot in general roll back the change already made to t1_.
--------------------

thanks in advance,
George

03-27-2008

grumpy

Yes and no.

You are correct if it is assumed that the structure of "Widget" is not allowed to change, as there is no way to make operations on T1 or T2 atomic.

However, if types T1 and T2 both provide the basic Abraham's guarantee or better, is is possible to implement Widget so it provides the strong guarantee, but that requires changing the structure of Widget (so it contains an auto_ptr<T1> and an auto_ptr<T2>). It is not possible to any useful guarantee without that structural change of Widget.

If either type T1 or type T2 fails to provide even the basic guarantee, then all bets are off, and it is not possible to implement Widget in an exception-safe manner.

03-27-2008

brewbuck

It seems doable if T1 and T2 have exception-free swap() methods. Then you could do this:

Abraham's exception theory are 3 leves, basic/strong/nothrow, in your bet below, which level do you assume?

Quote:

Originally Posted by grumpy

Yes and no.

You are correct if it is assumed that the structure of "Widget" is not allowed to change, as there is no way to make operations on T1 or T2 atomic.

However, if types T1 and T2 both provide the basic Abraham's guarantee or better, is is possible to implement Widget so it provides the strong guarantee, but that requires changing the structure of Widget (so it contains an auto_ptr<T1> and an auto_ptr<T2>). It is not possible to any useful guarantee without that structural change of Widget.

If either type T1 or type T2 fails to provide even the basic guarantee, then all bets are off, and it is not possible to implement Widget in an exception-safe manner.

Thanks brewbuck,

Quote:

Originally Posted by brewbuck

It seems doable if T1 and T2 have exception-free swap() methods. Then you could do this:

I've started getting in the habbit of providing non-throwing swap functions for my classes as well.

03-28-2008

George2

Hi iMalc,

What do you think is wrong with brewbuck' code?

I think the code below is correct, and it will catch any exception and rethrow the original exception. Any comments?

Code:

catch (...)
{
throw;
}

Quote:

Originally Posted by iMalc

This is for illustration purposes I assume?
The try block can of course be eliminated in this case.

I've started getting in the habbit of providing non-throwing swap functions for my classes as well.

regards,
George

03-28-2008

grumpy

Quote:

Originally Posted by George2

Abraham's exception theory are 3 leves, basic/strong/nothrow, in your bet below, which level do you assume?

I made no assumption. I meant that, if either T1 or T2 provides no guarantee (which, in practice, can make it impossible to implement a non-throwing swap()), then it is not possible to provide any guarantee in class Widget.

Also, actually, in Abrahams theory there are four levels: the other is "no guarantee".

The basic guarantee is the minimum necessary to be useful in practice (unless resource leaks are deemed acceptable). The strong guarantee implies the basic guarantee. The nothrow guarantee means that exceptions will not be thrown.

03-28-2008

George2

Thanks grumpy,

Let me summarize and confirm finally for this thread that, the root cause of why there is no stong exception safety assignment operator is neither T1 nor T2 could provide its own nothrow version public interface (e.g. swap), which guarantees to rollback to the original status during exception. Right?

Quote:

Originally Posted by grumpy

I made no assumption. I meant that, if either T1 or T2 provides no guarantee (which, in practice, can make it impossible to implement a non-throwing swap()), then it is not possible to provide any guarantee in class Widget.

Also, actually, in Abrahams theory there are four levels: the other is "no guarantee".

The basic guarantee is the minimum necessary to be useful in practice (unless resource leaks are deemed acceptable). The strong guarantee implies the basic guarantee. The nothrow guarantee means that exceptions will not be thrown.

regards,
George

03-28-2008

anon

Quote:

Originally Posted by George2

Hi iMalc,

What do you think is wrong with brewbuck' code?

I think the code below is correct, and it will catch any exception and rethrow the original exception. Any comments?

Code:

catch (...)
{
throw;
}

Wouldn't the exception propagate to the caller just the same without the try/catch block?

03-28-2008

George2

Hi anon,

I think we can do something inside try block, e.g. writing a log.

BTW, why do we discuss try/catch block, how do you think it relates to my original question?

Quote:

Originally Posted by anon

Wouldn't the exception propagate to the caller just the same without the try/catch block?

regards,
George

03-28-2008

grumpy

Quote:

Originally Posted by George2

Let me summarize and confirm finally for this thread that, the root cause of why there is no stong exception safety assignment operator is neither T1 nor T2 could provide its own nothrow version public interface (e.g. swap), which guarantees to rollback to the original status during exception. Right?

It's actually the reverse. If it is not possible to implement operations on T1 or T2 that provide required guarantees, then it is not possible for your "Widget" to provide guarantees if it uses those operations. That's just as true for whether the operation is a copy constructor or a swap() operation.

The lack of a non-throwing swap is not (specifically) a sign that a class cannot be used in a manner consistent with exception safety; it's lack could simply be an oversight rather than a sign that it could not be implemented. However, a non-throwing swap() operation is often provided as a means of achieving required exception safety for other operations.

03-28-2008

brewbuck

Quote:

Originally Posted by iMalc

This is for illustration purposes I assume?
The try block can of course be eliminated in this case.

Yes, I was just trying to make it clear that an exception might originate from that block of code. It isn't needed.

03-29-2008

George2

Thanks grumpy,

I think you mean the root cause is no exception safe swap, so we can not write exception safe assignment operator, right?

Quote:

Originally Posted by grumpy

It's actually the reverse. If it is not possible to implement operations on T1 or T2 that provide required guarantees, then it is not possible for your "Widget" to provide guarantees if it uses those operations. That's just as true for whether the operation is a copy constructor or a swap() operation.

The lack of a non-throwing swap is not (specifically) a sign that a class cannot be used in a manner consistent with exception safety; it's lack could simply be an oversight rather than a sign that it could not be implemented. However, a non-throwing swap() operation is often provided as a means of achieving required exception safety for other operations.

regards,
George

03-29-2008

grumpy

Quote:

Originally Posted by George2

I think you mean the root cause is no exception safe swap, so we can not write exception safe assignment operator, right?

No. There are three possible root causes of types T1 and T2 not offering exception safety guarantees.

1) They are designed or implemented without specific consideration of exception safety, and unable to be modified to achieve that. (i.e. a common practical scenario, particularly if the class is only available as a header file and a library without source code).

2) It is technically impossible to implement exception safety guarantees for the class (due to the nature of it's behaviour, reliance on other software, reliance on hardware, etc etc).

Non-existence of an exception-safe swap operation or of a exception-safe assignment operator (or a copy constructor) are consequences of one (or more) of the above, not causes.

Also, a non-throwing swap() operation is not a specific requirement to implement a exception-safe assignment operator. Using a copy constructor (which offers suitable guarantees) and a non-throwing swap() is a common and elegant way to implement an assignment operator with required guarantees, but there are other (less elegant) ways to do it.

03-30-2008

George2

Thanks grumpy,

Quote:

Originally Posted by grumpy

Also, a non-throwing swap() operation is not a specific requirement to implement a exception-safe assignment operator. Using a copy constructor (which offers suitable guarantees) and a non-throwing swap() is a common and elegant way to implement an assignment operator with required guarantees, but there are other (less elegant) ways to do it.

Can you show me other ways to do assignment operator without using nothrow swap and also making assignment operator exception safe please?