Why are there so many specializations of std::swap?

旧巷老猫 提交于 2020-01-13 07:36:07

问题


While looking at the documentation for std::swap, I see a lot of specializations.
It looks like every STL container, as well as many other std facilities have a specialized swap.
I thought with the aid of templates, we wouldn't need all of these specializations?

For example,
If I write my own pair it works correctly with the templated version:

template<class T1,class T2> 
struct my_pair{
    T1 t1;
    T2 t2;
};

int main() {
    my_pair<int,char> x{1,'a'};
    my_pair<int,char> y{2,'b'};
    std::swap(x,y);
} 

So what it is gained from specializing std::pair?

template< class T1, class T2 >
void swap( pair<T1,T2>& lhs, pair<T1,T2>& rhs );

I'm also left wondering if I should be writing my own specializations for custom classes,
or simply relying on the template version.


回答1:


So what it is gained from specializing std::pair?

Performance. The generic swap is usually good enough (since C++11), but rarely optimal (for std::pair, and for most other data structures).

I'm also left wondering if I should be writing my own specializations for custom classes, or simply relying on the template version.

I suggest relying on the template by default, but if profiling shows it to be a bottleneck, know that there is probably room for improvement. Premature optimization and all that...




回答2:


std::swap is implemented along the lines of the code below:

template<typename T> void swap(T& t1, T& t2) {
    T temp = std::move(t1); 
    t1 = std::move(t2);
    t2 = std::move(temp);
}

(See "How does the standard library implement std::swap?" for more information.)

So what it is gained from specializing std::pair?

std::swap can be specialized in the following way (simplified from libc++):

void swap(pair& p) noexcept(is_nothrow_swappable<first_type>{} &&
                            is_nothrow_swappable<second_type>{})
{
    using std::swap;
    swap(first,  p.first);
    swap(second, p.second);
}

As you can see, swap is directly invoked on the elements of the pair using ADL: this allows customized and potentially faster implementations of swap to be used on first and second (those implementations can exploit the knowledge of the internal structure of the elements for more performance).

(See "How does using std::swap enable ADL?" for more information.)




回答3:


Presumably this is for performance reasons in the case that the pair's contained types are cheap to swap but expensive to copy, like vector. Since it can call swap on first and second instead of doing a copy with temporary objects it may provide a significant improvement to program performance.




回答4:


The reason is performance, especially pre c++11.

Consider something like a "Vector" type. The Vector has three fields: size, capacity and a pointer to the actual data. It's copy constructor and copy assignment copy the actual data. The C++11 version also has a move constructor and move assignment that steal the pointer, setting the pointer in the source object to null.

A dedicated Vector swap implementation can simply swap the fields.

A generic swap implementation based on the copy constructor, copy assignment and destructor will result in data copying and dynamic memory allocation/deallocation.

A generic swap implementation based on the move constructor, move assignment and destructor will avoid any data copying or memory allocation but it will leave some redundant nulling and null-checks which the optimiser may or may not be able to optimise away.


So why have a specialised swap implementation for "Pair"? For a pair of int and char there is no need. They are plain old data types so a generic swap is just fine.

But what if I have a pair of say Vector and String ? I want to use the specialist swap operations for those types and so I need a swap operation on the pair type that handles it by swapping it's component elements.




回答5:


The most efficient way to swap two pairs is not the same as the most efficient way to swap two vectors. The two types have a different implementation, different member variables and different member functions.

There is no just generic way to "swap" two objects in this manner.

I mean, sure, for a copyable type you could do this:

T tmp = a;
a = b;
b = tmp;

But that's horrendous.

For a moveable type you can add some std::move and prevent copies, but then you still need "swap" semantics at the next layer down in order to actually have useful move semantics. At some point, you need to specialise.




回答6:


There is a rule (I think it comes from either Herb Sutter's Exceptional C++ or Scott Meyer's Effective C++ series) that if your type can provide a swap implementation that does not throw, or is faster than the generic std::swap function, it should do so as member function void swap(T &other).

Theoretically, the generic std::swap() function could use template magic to detect the presence of a member-swap and call that instead of doing

T tmp = std::move(lhs);
lhs = std::move(rhs);
rhs = std::move(tmp);

but no-one seems to have thought about that one, yet, so people tend to add overloads of free swap in order to call the (potentially faster) member-swap.



来源:https://stackoverflow.com/questions/41984587/why-are-there-so-many-specializations-of-stdswap

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!