问题
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