473,761 Members | 3,542 Online
Bytes | Software Development & Data Engineering Community
+ Post

Home Posts Topics Members FAQ

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 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! ]
Jul 22 '05 #1
7 2325
Fix: of course I mean

struct B
: public CanonicalReplac ement< B >

in code fragment 4.

-- Mikhail Kupchik

[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.m oderated. 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++.m oderated. 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++.m oderated. First time posters: Do this! ]
Jul 22 '05 #4
mi*****@mail.ru (Mikhail N. Kupchik) wrote in message news:<a0******* *************** ****@posting.go ogle.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 CanonicalReplac ement
{

BOOST_STATIC_AS SERT(boost::is_ base_and_derive d<CanonicalRepl acement,
T>::value);

CanonicalReplac ement& operator=(const CanonicalReplac ement&
other)
{
T& derived_this = static_cast<T&> (*this);
const T& derived_other = static_cast<con st T&>(other);

if(&derived_thi s != &derived_oth er) // this line is an
optimisation only
{
T temp(derived_ot her);

swap(derived_th is, 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++.m oderated. 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_pus h(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++.m oderated. First time posters: Do this! ]
Jul 22 '05 #6
jh*****@reflect ionsinteractive .com (James Hopkin) wrote in message news:<a0******* *************** ***@posting.goo gle.com>...
mi*****@mail.ru (Mikhail N. Kupchik) wrote in message news:<a0******* *************** ****@posting.go ogle.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 CanonicalReplac ement< T >.

The program below

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

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

if(&derived_thi s != &derived_oth er) // this line is an optimisation only
{
T temp(derived_ot her);

derived_this.sw ap(temp);
}
return *this;
}
};

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

struct B
: public CanonicalReplac ement< 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++.m oderated. First time posters: Do this! ]
Jul 22 '05 #7
> 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'.


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
CanonicalReplac ement<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 CanonicalReplac ement<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++.m oderated. 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
3353
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 like that possible? Some workaround? Thank you, Patrick
4
3916
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
3534
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 return. I tried to write something like this: template <class Type1, class Type2, class Type3> Type3 findMin(Type1 x, Type2 y){ return (x < y) ? x : y;
5
2180
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 that it takes a *long* time to compile and the "long double" version instantiates so many classes that gcc runs out of memory on my 1 gig machine ! So the question is - is there a simple way to reduce the number of template instantiations from...
2
2019
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. This makes it difficult to spot errors. For example, F4 to "jump to next error" just jumps ot the next "template assistance" message. And since there are hundreds of them, this is quite obnoxious. Can I disable these things? Why is MSVC...
2
4507
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
1828
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 struct cant_write_me{}; template< template< typename, typename class struct cant_write_me{}; template< typename struct arity_one{}; template< typename, typename struct arity_two{};
9
3479
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)); BOOST_STATIC_ASSERT((bin<1111>::value == 15)); BOOST_STATIC_ASSERT((bin<0>::value == 0)); BOOST_STATIC_ASSERT((bin<1010, 0011>::value == 163)); you can find my implementation at:
2
2214
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> class WidgetManager : public Creation Policy {...} // Create an instance of WidgetManager which managers type Widget WidgetManager< OpNewCreator<Widget MyWidgetMgr;
1
4466
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 http://cgi.ebay.de/ws/eBayISAPI.dll?ViewItem&item=250542673235 As you can see it can only be seen partially , can anyone help me with this? As fas as I can tell ebay's Iframe is messing up my layput, or am I wrong? Thx! here is the css/html code
0
9377
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 effortlessly switch the default language on Windows 10 without reinstalling. I'll walk you through it. First, let's disable language synchronization. With a Microsoft account, language settings sync across devices. To prevent any complications,...
0
10136
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, it seems that the internal comparison operator "<=>" tries to promote arguments from unsigned to signed. This is as boiled down as I can make it. Here is my compilation command: g++-12 -std=c++20 -Wnarrowing bit_field.cpp Here is the code in...
0
9989
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 tapestry of website design and digital marketing. It's not merely about having a website; it's about crafting an immersive digital experience that captivates audiences and drives business growth. The Art of Business Website Design Your website is...
1
9925
by: Hystou | last post by:
Overview: Windows 11 and 10 have less user interface control over operating system update behaviour than previous versions of Windows. In Windows 11 and 10, there is no way to turn off the Windows Update option using the Control Panel or Settings app; it automatically checks for updates and installs any it finds, whether you like it or not. For most users, this new feature is actually very convenient. If you want to control the update process,...
0
8814
agi2029
by: agi2029 | last post by:
Let's talk about the concept of autonomous AI software engineers and no-code agents. These AIs are designed to manage the entire lifecycle of a software development project—planning, coding, testing, and deployment—without human intervention. Imagine an AI that can take a project description, break it down, write the code, debug it, and then launch it, all on its own.... Now, this would greatly impact the work of software developers. The idea...
0
5266
by: TSSRALBI | last post by:
Hello I'm a network technician in training and I need your help. I am currently learning how to create and manage the different types of VPNs and I have a question about LAN-to-LAN VPNs. The last exercise I practiced was to create a LAN-to-LAN VPN between two Pfsense firewalls, by using IPSEC protocols. I succeeded, with both firewalls in the same network. But I'm wondering if it's possible to do the same thing, with 2 Pfsense firewalls...
0
5405
by: adsilva | last post by:
A Windows Forms form does not have the event Unload, like VB6. What one acts like?
1
3913
by: 6302768590 | last post by:
Hai team i want code for transfer the data from one system to another through IP address by using C# our system has to for every 5mins then we have to update the data what the data is updated we have to send another system
3
3509
muto222
by: muto222 | last post by:
How can i add a mobile payment intergratation into php mysql website.

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.