Hi All.
I have a question regarding Herb Sutter's idiom of implementation of
operator= via nonthrowing swap() member function, to guarantee strict
exception safety.
The idea of the idiom is shown by the example:
-- code fragment 1 ----------------------------------------------------
class MyClass
{
...
public:
void swap( MyClass& ) throw();
MyClass& operator=( const MyClass& rvalue )
{
MyClass other( rvalue ); // (1)
swap( other );
return *this;
}
};
-- end of code fragment 1 ---------------------------------------------
Thus we need to implement only copy constructor (used by (1)) and get
strict exception safety: every possible exception in operator= is
produced by (1) and gets thrown out before `this' object is actually
modified.
My question is the following: is it possible to implement the idiom of
exception-safe operator= via template class (with little help of CRTP
of course), so that I could write for many of my classes:
-- code fragment 2 ----------------------------------------------------
class MyClass
: public CanonicalReplac ement< MyClass >
{
...
public:
void swap( MyClass& ) throw();
};
-- end of code fragment 2 ---------------------------------------------
My first attempt was
-- code fragment 3 ----------------------------------------------------
template< typename T >
struct CanonicalReplac ement
{
T& operator=( const T& other )
{
if( this != &other ) // (2)
{
T temp_object( other ); // (3)
T::swap( temp_object ); // (4)
}
return *this;
}
};
-- end of code fragment 3 ---------------------------------------------
Besides of one slippery thing that in (2) `this' is implicitly upcasted
to CanonicalReplac ement<T>* and actually pointers to CanonicalReplac ement<T>
rather than T are compared (I'm in doubt if most compilers can optimize
all pointer arithmetic that arises here), there is one bigger trouble:
the code simply does not work, as the operator= is actually declared for
argument types ( CanonicalReplac ement<T>&, const T& ), not ( T&, const T& );
thus, compiler generates default version of it for class T.
The following example illustrates it:
-- code fragment 4 ----------------------------------------------------
struct A
{
A& operator=( const A& ) { cout << "operator= of A\n"; }
};
struct B
: public CanonicalReplac ement< A >
{
A a_;
B() { }
B( const B& ) { cout << "cctor of B\n"; }
void swap() throw() { cout << "swap of B\n"; }
};
int main()
{
B() = B(); // (5)
};
-- end of code fragment 4 ---------------------------------------------
In (5), if CanonicalReplac ement<B>::opera tor= is called, it will print
`cctor of B' in (3) then `swap of B' in (4).
But actually default B::operator= is generated, it calls A::operator= for
`a_' field and prints `operator= of A'.
Unfortunately it is not possible to use Barton-Nackman method of restricted
template expansion with aid of friend member function (see code fragment 5),
because operator= must be a member function (according to section 13.5.3.1
of the ISO standard for C++ programming language).
-- code fragment 5 ----------------------------------------------------
template< typename T >
struct CanonicalReplac ement
{
friend T& operator=( T& lvalue, const T& rvalue )
{
if( &lvalue != &rvalue )
{
T temp_object( rvalue );
lvalue.swap( temp_object );
}
return lvalue;
}
};
-- end of code fragment 5 ---------------------------------------------
The idion can be implemented via preprocessor, but this is a hack of course.
Does anybody have any other ideas on this?
-- Mikhail Kupchik
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.m oderated. First time posters: Do this! ]