473,387 Members | 1,650 Online
Bytes | Software Development & Data Engineering Community
Post Job

Home Posts Topics Members FAQ

Join Bytes to post your question to a community of 473,387 software developers and data experts.

Template-based implementation of Sutter's exception-safe operator= idiom

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 CanonicalReplacement< MyClass >
{
...
public:
void swap( MyClass& ) throw();
};
-- end of code fragment 2 ---------------------------------------------

My first attempt was

-- code fragment 3 ----------------------------------------------------
template< typename T >
struct CanonicalReplacement
{
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 CanonicalReplacement<T>* and actually pointers to CanonicalReplacement<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 ( CanonicalReplacement<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 CanonicalReplacement< 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 CanonicalReplacement<B>::operator= 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 CanonicalReplacement
{
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++.moderated. First time posters: Do this! ]
Jul 22 '05 #1
7 2302
Fix: of course I mean

struct B
: public CanonicalReplacement< B >

in code fragment 4.

-- Mikhail Kupchik

[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
Jul 22 '05 #2
mi*****@mail.ru (Mikhail N. Kupchik) writes:
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.


Herb doesn't deserve the blame for that technique (though maybe for
recommending it a bit too heartily). The swapping assignment operator
is almost always a bad idea, especially in a template, because it
spends cycles on the strong guarantee at what may be the wrong level
of granularity.

(http://lists.boost.org/MailArchives/boost/msg36928.php)

Any time you make assignment give the strong guarantee by copying and
swapping, you force anyone who wants to use assignment in an operation
which doesn't need that strong guarantee to pay for the unneccessary
copy, which could be very expensive.

--
Dave Abrahams
Boost Consulting
http://www.boost-consulting.com

[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
Jul 22 '05 #3
Oops: in my other reply I left in a paragraph about std::swap, which I
meant to remove.

Default std::swap is definitely *not* what we want, as that will
recursively call the assignment operator until the end of time.

James

[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
Jul 22 '05 #4
mi*****@mail.ru (Mikhail N. Kupchik) wrote in message news:<a0**************************@posting.google. com>...

Does anybody have any other ideas on this?


I'm not sure I'd want to do this (reasons listed below), but I can
suggest an implementation.

// T requirements: no-throw swap(T&, T&)
// no copy assignment operator defined

template< typename T >
struct CanonicalReplacement
{

BOOST_STATIC_ASSERT(boost::is_base_and_derived<Can onicalReplacement,
T>::value);

CanonicalReplacement& operator=(const CanonicalReplacement&
other)
{
T& derived_this = static_cast<T&> (*this);
const T& derived_other = static_cast<const T&>(other);

if(&derived_this != &derived_other) // this line is an
optimisation only
{
T temp(derived_other);

swap(derived_this, temp);
}
return *this;
}
};

This can be *privately* inherited, since assignment operators don't
get inherited anyway.

I used a namespace-scope swap, because this is more general. You can
always write a global swap to call a member one.

I'm calling a global std::swap, since this is more general (if you
want a member version to be used, you can define a global swap which
calls the member one).

To be extra safe, you can use boost::address_of rather than the &
operator, just in case the built-in & has been overridden.
As for why I wouldn't do this:

1) Assignment by swapping with temporary is a well-known idiom
(largely thanks to Herb - correct me if I'm wrong). Most coders will
immediately understand an assignment operator written this away, but
may puzzle for a while seeing this base. Of course, that problem goes
away if it were to become an accepted idiom (chicken and egg
situation).

2) Ease of mis-use: if the client defines a copy assignment
operator in T or any derived class without removing this base, things
go silently wrong. I can't think of a way of preventing this without
overhead.

3) Personal taste: I wouldn't want to use a base class to do
something so simple.

4) Potential inheritance overheads: with ideal compilers this
method would always be zero overhead, but in the real world we know
that's not always the case, particularly if you needed to inherit
other classes.

5) Compilation time: I would imagine instantiating this template
class everywhere would have some impact on compile times.
Cheers,
James

[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
Jul 22 '05 #5
David Abrahams <da**@boost-consulting.com> writes:
Any time you make assignment give the strong guarantee by copying
and swapping, you force anyone who wants to use assignment in an
operation which doesn't need that strong guarantee to pay for the
unneccessary copy, which could be very expensive.


For those who do need the strong guarantee at a higher level, how do
they go about getting it? I understand the "SGI argument" about
concurrency control, but I'm missing the analogous option here to add
exception-safe "locks" at a higher level.

Take your example where a client wants an assignment followed by
push_back() to have the strong guarantee.¹ Is this a potential
solution?
// Neither compiled nor tested.

template <typename C, typename T>
C& safe_assign_push(C& dest, C const& src, T const& val)
{
C temp( src );
temp.push_back( val );
dest.swap( temp );
return dest;
}
Footnotes:
¹ http://lists.boost.org/MailArchives/boost/msg36928.php

--
Steven E. Harris

[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
Jul 22 '05 #6
jh*****@reflectionsinteractive.com (James Hopkin) wrote in message news:<a0*************************@posting.google.c om>...
mi*****@mail.ru (Mikhail N. Kupchik) wrote in message news:<a0**************************@posting.google. com>...

Does anybody have any other ideas on this?


I'm not sure I'd want to do this (reasons listed below), but I can
suggest an implementation.


Hi.

Default operator= in T calls operator= for all base classes and fields, not only
for CanonicalReplacement< T >.

The program below

-- code fragment 6 ----------------------------------------------------

template< typename T >
struct CanonicalReplacement
{
CanonicalReplacement& operator=(const CanonicalReplacement& other)
{
T& derived_this = static_cast<T&> (*this);
const T& derived_other = static_cast<const T&>(other);

if(&derived_this != &derived_other) // this line is an optimisation only
{
T temp(derived_other);

derived_this.swap(temp);
}
return *this;
}
};

struct A
{
A& operator=( const A& ) { cout << "A::operator= (should not be called)\n"; }
};

struct B
: public CanonicalReplacement< B >
{
A a_;
B() { }
void swap( B& ) throw() { cout << "B::swap()\n"; }
B( const B& ) { cout << "cctor of B\n"; }
};

int main()
{
B() = B();
}

-- code fragment 6 ----------------------------------------------------

prints

cctor of B
B::swap()
A::operator= (should not be called)

-- Mikhail Kupchik

[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
Jul 22 '05 #7
> In (5), if CanonicalReplacement<B>::operator= 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'.


The problem is copy constructors are not inherited; it only seems that
way because if you don't define a copy constructor, the compiler tries
to chain the superclass one with the ones for each member.

You can kludgify it by creating an Replace member in
CanonicalReplacement<B> to do the copy and swap idiom, then calling
this from the defined copy constructor in B. Or even make Replace a
function template like std::swap is.

The only other thing I can think of (which is subtly icky on other
levels) is to reverse the inheritance:

1. Put all the core B stuff into B_core.
2. Make B inherit from CanonicalReplacement<B_core>, and ensure B
itself has no member variables etc.
3. Regularize your other constructors (that's the icky part).

Cheers,
Glen Low, Pixelglow Software
www.pixelglow.com

[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
Jul 22 '05 #8

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

6
by: Patrick Kowalzick | last post by:
Dear all, I have a question about default template parameters. I want to have a second template parameter which as a default parameter, but depends on the first one (see below). Is something...
4
by: Sebastian Faust | last post by:
Hi, I have 4 questions related to templates. I wanna do something like the following: template<typename T> class Template { public: Template_Test<T>()
31
by: nikola | last post by:
Hi all, I was working with a simple function template to find the min of two values. But since I would like the two values to be different (type) I dont know what kind of value (type) it will...
5
by: Gianni Mariani | last post by:
The spirit of this arguably pointless exercise, is that the numeric_limits<T> class could be replaced with a totally generic template of compile-time, template computed constants. The problem is...
2
by: Rudy Ray Moore | last post by:
Whenever I get any error with Vc++7.1/.net/2003, it is followed by huge ammounts of "template assistance" error messaging referencing template code (MTL) that has nothing to do with the error. ...
2
by: Alfonso Morra | last post by:
I have a class declared as ff: class __declspec(dllexport) A { public: A() ; A(const A&) A& operator=(const A&) ; ~A() ; void doThis(void) ;
4
by: Howard Gardner | last post by:
// I think that there is no way to write the template "cant_write_me" in // this example. Would love to be wrong. // one of many approaches that won't work template< template< typename class...
9
by: Leo jay | last post by:
i'd like to implement a class template to convert binary numbers to decimal at compile time. and my test cases are: BOOST_STATIC_ASSERT((bin<1111,1111,1111,1111>::value == 65535));...
2
by: Gary Nastrasio | last post by:
I'm currently reading Andrei Alexandrescu's book "Modern C++ Design" and I'm a bit confused by one bit of template syntax in chapter 1. Here is a code example: template <class CreationPolicy>...
1
by: matz2k | last post by:
I've got a big problem with the CSS layout which I've produced with Photoshop/Dreamweaver especially for my ebay auctions. This is what it looks like...
0
by: Charles Arthur | last post by:
How do i turn on java script on a villaon, callus and itel keypad mobile phone
0
by: aa123db | last post by:
Variable and constants Use var or let for variables and const fror constants. Var foo ='bar'; Let foo ='bar';const baz ='bar'; Functions function $name$ ($parameters$) { } ...
0
by: ryjfgjl | last post by:
If we have dozens or hundreds of excel to import into the database, if we use the excel import function provided by database editors such as navicat, it will be extremely tedious and time-consuming...
0
BarryA
by: BarryA | last post by:
What are the essential steps and strategies outlined in the Data Structures and Algorithms (DSA) roadmap for aspiring data scientists? How can individuals effectively utilize this roadmap to progress...
1
by: Sonnysonu | last post by:
This is the data of csv file 1 2 3 1 2 3 1 2 3 1 2 3 2 3 2 3 3 the lengths should be different i have to store the data by column-wise with in the specific length. suppose the i have to...
0
marktang
by: marktang | last post by:
ONU (Optical Network Unit) is one of the key components for providing high-speed Internet services. Its primary function is to act as an endpoint device located at the user's premises. However,...
0
by: Hystou | last post by:
Most computers default to English, but sometimes we require a different language, especially when relocating. Forgot to request a specific language before your computer shipped? No problem! You can...
0
Oralloy
by: Oralloy | last post by:
Hello folks, I am unable to find appropriate documentation on the type promotion of bit-fields when using the generalised comparison operator "<=>". The problem is that using the GNU compilers,...
0
jinu1996
by: jinu1996 | last post by:
In today's digital age, having a compelling online presence is paramount for businesses aiming to thrive in a competitive landscape. At the heart of this digital strategy lies an intricately woven...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.