The Copy&Swap Idiom

Started by
31 comments, last by TheComet 10 years, 6 months ago

But wait a second, what if the user uses boost::swap instead of std::swap? Or better yet, a custom implementation of swap?

I suppose that's their own fault for not using the build in method Foo::swap.

"I would try to find halo source code by bungie best fps engine ever created, u see why call of duty loses speed due to its detail." -- GettingNifty
Advertisement

boost::swap will call std::swap() if ADL doesn't look-up swap() in the namespace of the arguments.

The user should call unqualified swap, so that ADL has a chance to kick in. So swap should be a free standing function in the same namespace as the class it swaps.

The user should call unqualified swap, so that ADL has a chance to kick in. So swap should be a free standing function in the same namespace as the class it swaps.

Emphasis mine.
Are you sure about that? The C++ standard explicitly allows for template specializations (not overloads) to be added to the std namespace.

The behavior of a C++ program is undefined if it adds declarations or definitions to namespace std or to a namespace within namespace std unless otherwise specified. A program may add a template specialization for any standard library template to namespace std only if the declaration depends on a user-defined type and the specialization meets the standard library requirements for the original template and is not explicitly prohibited.


I'm not actually sure what is considered best practice anymore.
if you think programming is like sex, you probably haven't done much of either.-------------- - capn_midnight

This is off topic, but: in 99% of the C++ projects I've worked on, exceptions have been banned by the programming guidelines/standards laugh.png
Writing exception-safe code is very important if you're writing the standard library that will be used by everyone, but in my own experience, it's not something that is required in order to work on most C++ projects.

That's odd, because I've been encouraged to use exceptions if it makes sense. There's an entire section on when and when not to use exceptions here: http://www.parashift.com/c++-faq-lite/exceptions.html


I believe, though I could be wrong, that most video game consoles (at least on the 360 and ps3) have poor exception support, so most game programmers (who are generally targeting at least one of those platforms) don't use exceptions.
if you think programming is like sex, you probably haven't done much of either.-------------- - capn_midnight

I really think it only makes sense to throw an exception when you've got a ctor error and you want to catch it somewhere. Otherwise I only really use them to crash the program in such a way that will take me directly to the error (just throwing a string literal). Apart from that, I find them to be more of a PITA and a bogeyman than anything else.


I was reading up on the rule of 3, and found a more elegant way of overriding the assignment operator. The way I initially learned to do it was something like this:


class Foo
{
public:
  Foo& operator=( const Foo& cp )
  {
    if( this == &cp ) return *this;
    // copy resources here
    return *this;
  }
};
HOWEVER, this is not exception safe (for instance, if you are allocating new objects during the copying of resources, and any one of them throws an exception, you'll be looking at a memory leak) and it performs needless checks for self assignment.

Oh? Here you go...


class Foo {
public:
  Foo& operator=(const Foo& cp) {
    if(this == &cp) return *this;
    //use RAII here
    return *this;
  }
};

Problem solved!

I get the idea of copy and swap, but you're really just offloading the work to the copy ctor, aren't you? You could just as easily write the code into the assignment operator and then write the copy ctor to just call the assignment operator. If you're copying resources then you have to write the explicit copy behaviors somewhere.

void hurrrrrrrr() {__asm sub [ebp+4],5;}

There are ten kinds of people in this world: those who understand binary and those who don't.

The user should call unqualified swap, so that ADL has a chance to kick in. So swap should be a free standing function in the same namespace as the class it swaps.

Emphasis mine.
Are you sure about that? The C++ standard explicitly allows for template specializations (not overloads) to be added to the std namespace.

The behavior of a C++ program is undefined if it adds declarations or definitions to namespace std or to a namespace within namespace std unless otherwise specified. A program may add a template specialization for any standard library template to namespace std only if the declaration depends on a user-defined type and the specialization meets the standard library requirements for the original template and is not explicitly prohibited.


I'm not actually sure what is considered best practice anymore.

I suppose it doesn't matter too much which approach a the implementer of swap is using. On the user side, that's where you want to make sure ADL is supported, especially for template code that isn't aware of the types it's instantiated with. So given such support, I think using ADL is a bit more elegant than adding things to namespace std, especially if the implementation of swap needs to access the private interface of the class -- that is, it's a friend function.

The user should call unqualified swap, so that ADL has a chance to kick in. So swap should be a free standing function in the same namespace as the class it swaps.

Emphasis mine.
Are you sure about that? The C++ standard explicitly allows for template specializations (not overloads) to be added to the std namespace.

You're not writing classes in the std namespace, so you don't put your custom swap functions in the std namespace. If you're writing classes in namespace mine, then your swap function also goes in namespace mine.

[edit] I misread Chaos' post wacko.png

Again, sorry for off-topic:

his is off topic, but: in 99% of the C++ projects I've worked on, exceptions have been banned by the programming guidelines/standards laugh.png
Writing exception-safe code is very important if you're writing the standard library that will be used by everyone, but in my own experience, it's not something that is required in order to work on most C++ projects.

That's odd, because I've been encouraged to use exceptions if it makes sense. There's an entire section on when and when not to use exceptions here: http://www.parashift.com/c++-faq-lite/exceptions.html
I believe, though I could be wrong, that most video game consoles (at least on the 360 and ps3) have poor exception support, so most game programmers (who are generally targeting at least one of those platforms) don't use exceptions.

Yeah, there's a lot of historic reasons why exceptions in C++ aren't popular. When writing in other languages, I definitely follow the kind of guidelines you mention about when to use exceptions... but in C++ I'm usually simply not allowed to use them.

Compared to other languages, C++ exceptions also have the caveat that you often throw by value and catch by reference, in order to avoid memory management issues. In C# on the other hand, you'd use new to create the exception, and it would later be garbage collected.

In C++ this creates some rare issues, such as if you want to store a polymorphic exception -- e.g. if an exception results in a thread terminating, you'd likely want to clone it and then propagate it to the creating thread, re-throwing the exception there when it waits for completion / joins...

In the past, C++ compilers have done a pretty poor job of implementing exceptions. On PPC (consoles) it's been especially bad, with the console SDKs often encouraging you to use the command-line options to disable exceptions altogether. Even most x86 implementations aren't the best, though x86-64 has improved things.
Depending on the implementation, simply having exceptions enabled adds a lot of bloat to every single function, which is an expense you pay for regardless of whether you even throw or not. On machines with small instruction caches (e.g. old consoles), this was especially bad, and it completely violates the whole justification that "exceptions are free unless you throw".
This wasn't just in the games industry either, many other "embedded systems" fared just as poorly as games consoles.

Also on games consoles and embedded systems, there's often not much you can do in cases where an unexpected error occurs. You've got code there to deal with the expected errors, but if something unforeseen happens, often you only option is to log the error / make a debug dump and then crash hard / reboot. If this is your strategy, there will be other platform-specific ways to install a crash handler that are more effective / performant.

C++ compilers often also have two different exception handling mechanisms. e.g. MSVC has try/catch and __try/__except... The former catches things that are thrown by the programmer, and the latter catches things that are reported by the kernel, like invalid memory accesses. In C# on the other hand, all errors are handled by the standard try/catch/finally keywords. The lack of a finally in C++ also forces you to use RAII, instead of having the option to reconcile manual resource management with exception handling.

Then there's the added complexity required when writing exception-safe code. There's 4 levels -- no-throw, strong (nothing will break if an exception is thrown), weak (nothing will leak if an exception is thrown, but data might become invalid) and none (no promises, throw at your own risk).
Perfectly reusable C++ code (such as the standard library) should ideally have strong exception safety. However, writing C++ code is already complex, and writing strongly-exception-safe code makes it even more complex. In every single function you need to keep in mind what the state of you invariants will be at each line of the program, and make sure that members/data/etc are only used in a read-process-commit fashion at all times.
Simply choosing to only provide the weak guarantee by using RAII for all resources reduces the burden on the programmer. Choosing to not give any guarantees reduces it much further.

If you know for certain that a function will only ever exit at the end or at a return statement, it's much easier to analyse / mentally-debug your code.

When doing a cost benefit analysis, many companies have decided that the benefits that C++ exceptions bring do not outweigh the added costs to code authoring and maintenance and/or performance (on certain systems).

This then has a knock-on effect for library authors. If you know that some percentage of your clients are disabling support for the throw/catch keywords in their compilers, and you want to keep those customers, then you've got to remove the usage of throw/catch from your library (or make it optional). There's also a legacy effect, where if you're reusing a ton of code with no exception safety guarantees, then it's dangerous to decide to begin adding exceptions later on...
The end result of this in my experience has been a decade of writing C++ code without very much professional experience at all with C++ exceptions. It's still something that's good to have an academic grip on though wink.png

However, writing C++ code is already complex, and writing strongly-exception-safe code makes it even more complex.

To be fair, writing such code isn't just an issue where exceptions are used. Writing code without involving exceptions, that can just as equally fail to acquire resources for the same reasons, needs about as much work to ensure correctness.

"In order to understand recursion, you must first understand recursion."
My website dedicated to sorting algorithms

This is for everyone really, a summary as to why copy & swap should be used over manually assigning:


I get the idea of copy and swap, but you're really just offloading the work to the copy ctor, aren't you? You could just as easily write the code into the assignment operator and then write the copy ctor to just call the assignment operator. If you're copying resources then you have to write the explicit copy behaviors somewhere.
  • The Big Rule Of 3 states: If you need either a copy-constructor, destructor, or overload the assignment operator, you will need all 3. http://en.wikipedia.org/wiki/Rule_of_three_%28C++_programming%29 This is always the case if your class manually manages resources (e.g. uses 'new' for its member variables).
    It is good practice to have a swap method for such classes in addition to all of that (the "3 and a half rule", some people call it). So with all of this in place, it is usually easier to write a copy&swap implementation and offloading the workload to the copy constructor rather than duplicating your copying code.
  • A copy-constructor's initialisation list will execute noticably faster than manually assigning values. http://stackoverflow.com/questions/13894415/c-creating-objects-initialization-lists-vs-assignment
  • When using copy & swap, no needless self-assignment checks have to be performed ( if( &cp == this ) return *this; )
  • copy & swap is guaranteed to be exception safe.

The bottom line is: There are more pro's than con's for a copy&swap over the traditional method.

"I would try to find halo source code by bungie best fps engine ever created, u see why call of duty loses speed due to its detail." -- GettingNifty

This topic is closed to new replies.

Advertisement