Why doesn't std :: swap use a swap idiom?

How to use std :: swap correctly:

using std::swap; swap(a,b); 

This is a bit verbose, but it ensures that if a, b is better defined by swap, it will be selected.

So now my question is why std::swap not implemented using this method, so will the user code just need to call std::swap ?

So something like this (ignoring noexcept and limitations for brevity):

 namespace std { namespace internal { template <class T> // normal swap implementation void swap(T& a, T& b) { // not intended to be called directly T tmp = std::move(a); a = std::move(b); b = std::move(tmp); } } template <class T> void swap(T& a, T& b) { using internal::swap; swap(a,b); } } 
+7
c ++ argument-dependent-lookup template-function
source share
3 answers

It falls into the territory of the tautology, but it is not, because it is not its purpose.

The purpose of std::swap is to be a swap function of last resort. This should not be what you call directly, unless you really want to use the last swap.

Now you can argue that what you offer is the best paradigm for tuning points. Looking back, always 20/20; not everything STL did was the right idea (see vector<bool> ).

As for why we cannot change it now, this is another matter. std::swap is the last-instance std::swap function. Therefore, it is theoretically possible that people call it and expects it to bypass any custom exchange code. And therefore, changing it in this way violates their code.

+6
source share

Here is one problem with this approach. A two-stage ADL relies on a function that ADL considers more appropriate than normal (otherwise you will get an overload rejection). This is usually great, because when you write swap() for your custom types in your custom namespaces, you write functions specific to those types.

But for standard types of libraries that may not have a more efficient swap() than a simple algorithm, this breaks. Example:

 namespace N { namespace internal { template <typename T> void swap(T&, T&); // normal swap impl } template <typename T> void swap(T& a, T& b) { using internal::swap; swap(a, b); // (*) } struct C { }; } N::C c; swap(c, c); // error N::swap(c, c); // error 

Both of these calls fail for the same reason. An unqualified call to swap() marked (*) will find N::internal::swap() through a regular unqualified search, and then find N::swap() through ADL. You cannot distinguish between these calls, since you really need both calls to work for all types that satisfy the swap constraints. The call received is ambiguous.

Thus, such a construction will require the addition of a new swap() function for each type in namespace std , even if it simply goes to std::internal::swap() .

+3
source share

Historically, it seems that there was not much thought about name resolution. std::swap was designed as a configuration point, but it was also launched as a function that could be called in the general code for data exchange. Therefore, if std::swap did not work or was too slow, then it might have to overload it even if an excellent swap was found with ADL. This design cannot be changed without breaking or changing the meaning of existing code. Now there have been cases when the committee gladly decided to change the value of the existing code for the sake of performance, for example, the semantics of implicit movement (for example, when transmitting temporary values ​​by value or RVO, where elite is not implemented). So this is not entirely clear. However, changing std::swap from a configuration point to a name resolution wrapper with all existing existing std::swap overloads is questionable. This can lead to catastrophic errors in poorly written legacy code.

Another important reason is IMO, that you cannot move this idiom into the std , while maintaining your commonality. For example:.

 namespace A { struct X{}; } namespace B { using std::swap; void swap(A::X&, A::X&); template<typename T> void reverse(T (&ts)[4]) { swap(ts[0], ts[3]); swap(ts[1], ts[2]); } void silly(A::X (&xs)[4]) { reverse(xs); } } 

Here silly ends with B::swap . The reason for this is that std::swap (via using ) and B::swap displayed with the same priority, but the latter is better. Now you can argue that this is a stupid example, so here is a little more, which is a bit far-fetched:

 namespace types { /*...*/ } namespace algorithms { /*including some swap implementations for types from above...*/ } template<typename T> void reverse(T (&ts)[4]) { using std::swap; using algorithms::swap; swap(ts[0], ts[3]); swap(ts[1], ts[2]); } 

This will use the swap function from algorithms if it is better than any overload of std::swap , but algorithms::swap will not be found by ADL, so the search cannot happen inside std::swap , in the general case. An arbitrary number of namespaces can be involved here.

+2
source share

All Articles