void pair::swap(pair& p) noexcept(noexcept(swap(first, p.first)) && noexcept(swap(second, p.second)));

For a user-defined type it is now much more preferable to have its own namespace-level swap overload instead of using vanilla std::swap. The namespace-level swap function, being an extension of the type’s interface, is vastly more efficient and often can be written in way that it does not throw. The std::swap function, on the other hand, may throw because it uses a copy-constructor and copy-assignment operator while swapping two objects. If you are like me, your member copy-assignment operator will use copy-and-swap idiom for strong exception safety. That means std::swap will create and destroy 3 copies. What a waste! Moreover, that increases the chance of throwing. Bottom line: If T has a member swap, a swap function should be added in the same namespace as of T.

There are some assumptions buried in the above reasoning: (1) move-constructor is not available for a user-defined T, and (2) it is possible to implement member swap of T that does not throw and you can actually say so.

As far as move-constructor being not available is concerned, I think, there are large number of C++03 libraries, which will acquire move-semantics slowly than you might desire. Till then std::swap will, unfortunately, use copy-ctor and copy-assign. A legacy class with a copy-ctor won’t get an automatic move-ctor because it would do wrong things.

The noexcept specification of the user-defined swap better be accurate. Can we really write a nothrow member swap and say so confidently? Obviously, it depends on the members of the user-defined type that we’re swapping. To me, it’s all uphill from here.

void tuple::swap(tuple& rhs) noexcept(see below) // The expression inside noexcept is equivalent to the // logical AND of the following expressions: noexcept(swap(declval<T&>(), declval<T&>())) // where Ti is ith type in the variadic list of types.

namespace L { struct legacy { legacy(); // Does not throw legacy(const legacy &); // This may throw legacy & operator = (const legacy &); // This may throw (strong exception safe) ~legacy() throw(); // Does not throw void swap(legacy &) throw(); // Does not throw }; } // namespace L

namespace L { void swap(legacy &, legacy &) noexcept; } namespace std { template <> void swap(L::legacy &, L::legacy &) noexcept; }

namespace M { struct Modern { int count; std::string str; std::array<L::legacy, 5> data; /// default-ctor, copy-ctor, copy-assign, move-ctor, move-assign are defined = default. void swap(Modern & p) noexcept(noexcept(swap(std::declval<int &>(), std::declval<int &>())) && noexcept(swap(std::declval<std::string &>(), std::declval<std::string &>())) && noexcept(swap(std::declval<std::array<L::legacy, 5> &>(), std::declval<std::array<L::legacy, 5> &>()))); }; } // namespace M

Note: The text in italics below is not accurate anymore as noted in the comment below. C++11 std::swap uses move-construction and move-assignment internally. As a result, std::swap in C++11 is generally very efficient IF the move-operations are defined and they are really more efficient than their copy counterparts. In such cases, there is little need to write a custom namespace-level swap. However, member swap may still be useful. See the "default-construct-and-swap" technique for implementing a move-constructor below.



Couple of important things to note here:

Because we added namespace-level swap for the L::legacy class, we dodged several problems.If we did not have free swap, the compiler will instantiate std::swap if it is in the scope. Note, however, that vanilla std::swap may throw defeating the purpose of Modern::swap. If no swap is to be found, compiler issues an error.

We hope that swap of std::string and std::array do not throw. As mentioned in [container.requirements.general], std::string::swap does not throw but std::array<T> swap may throw if an unqualified call to swap(T &, T &) throws. Once again, the namespace-level swap will be chosen here, if available. If that does not exist, a specialization of std::swap will be chosen. If none of them exist, std::swap will be instantiated giving our Modern::swap a nothrow(false) specification.

void Modern::swap(Modern &) noexcept { static_assert(noexcept(swap(std::declval<int &>(), std::declval<int &>())) && noexcept(swap(std::declval<std::string &>(), std::declval<std::string &>())) && noexcept(swap(std::declval<std::array<L::legacy, 5> &>(), std::declval<std::array<L::legacy, 5> &>())), "throwing swap"); //.... remaining swap code }

void Modern::swap(Modern & that) noexcept { typedef std::tuple<int, std::string, std::array <L::legacy, 5> > MemberTuple; static MemberTuple *t = 0; static_assert(noexcept(t->swap(*t)), "throwing swap"); // .... remaining swap code }

is_nothrow_swappable_all

template<typename... T> struct is_nothrow_swappable_all { static std::tuple<T...> *t = 0; enum { value = noexcept(t->swap(*t)) }; }; void Modern::swap(Modern & that) noexcept(is_nothrow_swappable_all<int, std::string, std::array<L::legacy, 5> >::value);

void Modern::swap(Modern & that) noexcept(is_nothrow_swappable_all<int, std::string, std::array<L::legacy, 5> >::value); { using std::swap; swap(this->count, that->count); swap(this->str, that->str); swap(this->data, that->data); }

::resize is not as fast as it can be. We can do better.



Implementing a move-ctor using the default-construct-and-swap idiom turns out be quite handy. Default-construct-and-swap does what it says by first delegating to a noexcept default constructor followed by a swap. To implement a noexcept move-ctor this way, you really need to make sure that swap does not throw. As always, you can rely on a conditional noexcept. I'm using std::is_nothrow_constructible type trait to test the obvious!

Modern::Modern(Modern && m) noexcept(std::is_nothrow_constructible<Modern>::value && noexcept(m.swap(m))) : Modern() { swap(m); }

Modern::operator = (Modern && m) noexcept { swap(m); }