Proposal: is_swapable, is_nothrow_swapable, nothrow_swap, swap

Hi boosters, We often need to swap contents of two objects, but even more often we need to swap contents with no-throw exception guarantee. But currently we have no general way to swap two objects with no-throw exception guarantee. What we do have now is std::swap, and member functions which can be named swap, Swap, and so on. But now we can't swap arbitary types with no-throw exception guarantee in a general way. Yes, we can overload std::swap to support other than std:: types, but we can't guarantee that invocation of std::swap won't throw. So, I propose the following: New traits classes: boost::is_swappable<T>::value, boost::is_nothrow_swappable<T>::value. and specialize them for known types, but by default both should return false. Introduce new function boost::nothrow_swap(T &, T &); which will swap two objects if boost::is_nothrow_swappable<T>::value == true, else compile-time error will occur. Introduce new function boost::swap(T &, T &); which will swap two objects if boost::is_swappable<T>::value == true, else compile-time error will occur. Introduce template<typename T> struct do_nothrow_swap { static void nothow_swap (T &, T&); } and specialize it for types that can be swapped with no-throw guarantee such as std::vector, invoking default implementation will lead to compile-time error. Introduce template<typename T> struct do_swap { static void swap(T &, T &); }; and specialize it for types that can be swapped(but without guarantees) such as boost::array, invoking do_nothow_swap<T>::nothow_swap if no specialization found. Using this technique, we can support even such classes that doesn't have swap member-function, but have Swap or any other. I guess it would be helpful at least in boost::ptr_container library. This gives new standard way to do the swapping work, and in contrast to std::swap now we can be expilictly specify no-throw guarantee if needed. As always any comments are appreciated. P.S. Sorry if it was already proposed, didn't found it. -- Pavel Chikulaev

Hi boosters,
We often need to swap contents of two objects, but even more often we need to swap contents with no-throw exception guarantee.
But currently we have no general way to swap two objects with no-throw exception guarantee. What we do have now is std::swap, and member functions which can be named swap, Swap, and so on. But now we can't swap arbitary types with no-throw exception guarantee in a general way.
Yes, we can overload std::swap to support other than std:: types, but we can't guarantee that invocation of std::swap won't throw.
My opinion is that we should only have one swap function per object type named swap. And we should always make it such that it won't throws. I think that the best thing would be that std::swap would only compile for a given type T if the resulting function cannot throws (because copy constructor and assigment would not throws) and thus having a type trait would not be as much usefull. OTOH, if we add type trait to indicate if swap is possible or if it will throw, then the defaut implementation should be able to deduce it from the traits of the copy constructor and assignment operator. In particular swap of POD types does not throws. Also the compiler should be able to detect throw () specification and provide the type trait itself. Philippe

"Philippe Mori" <philippe_mori@hotmail.com> wrote:
My opinion is that we should only have one swap function per object type named swap. And we should always make it such that it won't throws.
What if class only has Swap function? What to do then? Or swap can throw? (boost::array for example)
I think that the best thing would be that std::swap would only compile for a given type T if the resulting function cannot throws (because copy constructor and assigment would not throws) and thus having a type trait would not be as much usefull.
It's not possible since std::swap implementated for not specialized type as follows: T t(a); a = b; b = t; So, we can be sure in anything. Will it throw or not? Is it optimal? We just can't answer these questions.
OTOH, if we add type trait to indicate if swap is possible or if it will throw, then the defaut implementation should be able to deduce it from the traits of the copy constructor and assignment operator. In particular swap of POD types does not throws.
Yeah, some assumptions can be made, but not only for POD types and some other trivial cases. So we need to explicitly register type.
Also the compiler should be able to detect throw () specification and provide the type trait itself.
Too many people don't use throw specifications, but their swap still not throws. Thanks for comments, Philippe. -- Pavel Chikulaev

My opinion is that we should only have one swap function per object type named swap. And we should always make it such that it won't throws.
What if class only has Swap function? What to do then? Or swap can throw? (boost::array for example)
Well I did not know about boost::array that could throw so I was assuming that "standard" swap were never throwing...
Yeah, some assumptions can be made, but not only for POD types and some other trivial cases. So we need to explicitly register type.
Well, with some compiler supports, we can know in some situation that it would not throw particulary if the compiler (or library) already implement some type traits like has_no_throw_copy<T>... But effectively, I guess that most of the time when swap is defined specifically for a type, it won't throws and the compiler won't knows. So the best thing that the compiler would probably be able to do (if no specific traits exist for a given type T) is to detect if the standard implementation is used and if so the type trait would be implemented in term of has_no_throw_assign<T> and has_no_throw_copy<T>.
Also the compiler should be able to detect throw () specification and provide the type trait itself.
Too many people don't use throw specifications, but their swap still not throws.
If the specification is used and it is possible to know it (on that implentation) then the default would be deduce from that instead of always being false. User can always provide a specilization if needed. I think that for consistency, the trait should be has_nothrow_swap<T> (see C++ Template Metaprogramming page 27).

"Philippe Mori" <philippe_mori@hotmail.com> wrote in message news:BAY20-DAV16330862113398AB9C09D198340@phx.gbl...
[snipped] Agreed with every word. I even like your has_nothow_swap more than initial name. Thanks.
I think that for consistency, the trait should be has_nothrow_swap<T> (see C++ Template Metaprogramming page 27). Unfortunately, I still don't have this book, but believe it will ship in next 2-3 days.
-- Pavel Chikulaev

Well I did not know about boost::array that could throw so I was assuming that "standard" swap were never throwing... Copy ctor can throw, assign operator can throw two times, dtor can
"Philippe Mori" <philippe_mori@hotmail.com> wrote in message news:BAY20-DAV16330862113398AB9C09D198340@phx.gbl... throw (actually we should forget about this case), so we can't never be sure about it. -- Pavel Chikulaev

Philippe Mori wrote:
I think that the best thing would be that std::swap would only compile for a given type T if the resulting function cannot throws (because copy constructor and assigment would not throws) and thus having a type trait would not be as much usefull.
This implies that swap() would only be possible to implement in a non-throwing fashion if 1. the cctor and the copy assignment operator are accessible and 2. cctor and copy assignment operator do not throw. This requirement is way too strong. E.g. the cctor of boost::array can throw, because it allocates memory. However, the swap() function can guarantee non-throwing operation when swap() for the value_type operates non-throwing, too. Even accessibility of a copy-ctor or a copy-assignment operator aren't required. Classes like {i/o}stream aren't copyable. However, you could imagine to swap objects of types which implement similar concepts.
Also the compiler should be able to detect throw () specification and provide the type trait itself.
Are throw specifications used for anything other then the dtors of classes derived from std::exception? Many libraries don't use them and they have reasons to avoid them. Regards, m
participants (3)
-
Martin Wille
-
Pavel Chikulaev
-
Philippe Mori