为什么 std::swap 有这么多专业?
Why are there so many specializations of std::swap?
在查看 std::swap
的文档时,我看到了很多专业化内容。
看起来每个 STL 容器以及许多其他标准工具都有专门的交换。
我想在模板的帮助下,我们不需要所有这些专业化?
例如,
如果我自己编写 pair
它可以与模板版本一起正常工作:
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);
}
那么专业化有什么好处std::pair
?
template< class T1, class T2 >
void swap( pair<T1,T2>& lhs, pair<T1,T2>& rhs );
我还想知道我是否应该为自定义 类、
编写自己的专业化
或者简单地依赖于模板版本。
So what it is gained from specializing std::pair?
性能。通用交换通常足够好(自 C++11 起),但很少是最优的(对于 std::pair
和大多数其他数据结构)。
I'm also left wondering if I should be writing my own specializations for custom classes, or simply relying on the template version.
我建议默认依赖模板,但如果分析显示它是一个瓶颈,请知道可能还有改进的余地。过早的优化等等...
大概这是出于性能原因,因为 pair
包含的类型交换成本低但复制成本高,例如 vector
。由于它可以在 first
和 second
上调用交换而不是使用临时对象进行复制,因此它可以显着提高程序性能。
std::swap
是按照以下代码行实现的:
template<typename T> void swap(T& t1, T& t2) {
T temp = std::move(t1);
t1 = std::move(t2);
t2 = std::move(temp);
}
(有关详细信息,请参阅 "How does the standard library implement std::swap?"。)
So what it is gained from specializing std::pair
?
std::swap
可以通过以下方式特化 (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);
}
如您所见,swap
是使用 ADL 直接在对的元素上调用的:这允许在 first
上使用 swap
的自定义且可能更快的实现second
(这些实现可以利用元素内部结构的知识来提高性能)。
(有关详细信息,请参阅 。)
交换两个对的最有效方法与交换两个向量的最有效方法不同。两种类型有不同的实现,不同的成员变量和不同的成员函数。
没有以这种方式 "swap" 两个对象的通用方法。
我的意思是,当然,对于可复制类型,您可以这样做:
T tmp = a;
a = b;
b = tmp;
但这太可怕了。
对于可移动类型,您可以添加一些 std::move
并防止复制,但是您 仍然 需要 "swap" 下一层的语义以便实际具有有用的移动语义。在某些时候,您需要专攻。
原因是性能,尤其是 pre c++11。
考虑类似 "Vector" 的类型。 Vector 具有三个字段:大小、容量和指向实际数据的指针。它是复制构造函数和复制赋值复制实际数据。 C++11版本还有一个移动构造函数和移动赋值窃取指针,将源对象中的指针设置为空。
专用的 Vector 交换实现可以简单地交换字段。
基于复制构造函数、复制赋值和析构函数的通用交换实现将导致数据复制和动态内存allocation/deallocation。
基于移动构造函数、移动赋值和析构函数的通用交换实现将避免任何数据复制或内存分配,但它会留下一些冗余的空化和 null-checks 优化器可能会也可能不会优化掉。
那么为什么要为 "Pair" 提供专门的交换实现呢?对于一对 int 和 char 没有必要。它们是普通的旧数据类型,因此通用交换就可以了。
但是如果我有一对 Vector 和 String 呢?我想对这些类型使用专门的交换操作,因此我需要对通过交换它的组件元素来处理它的对类型进行交换操作。
有一条规则(我认为它来自 Herb Sutter 的 Exceptional C++ 或 Scott Meyer 的 Effective C++ 系列),如果您的类型可以提供不抛出或比泛型更快的交换实现 std::swap
函数,它应该作为成员函数 void swap(T &other)
.
理论上,通用 std::swap()
函数可以使用模板魔术来检测 member-swap 的存在并调用它而不是执行
T tmp = std::move(lhs);
lhs = std::move(rhs);
rhs = std::move(tmp);
但是 no-one 似乎已经考虑过那个,所以人们倾向于添加免费 swap
的重载以便调用(可能更快)member-swap。
在查看 std::swap
的文档时,我看到了很多专业化内容。
看起来每个 STL 容器以及许多其他标准工具都有专门的交换。
我想在模板的帮助下,我们不需要所有这些专业化?
例如,
如果我自己编写 pair
它可以与模板版本一起正常工作:
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);
}
那么专业化有什么好处std::pair
?
template< class T1, class T2 >
void swap( pair<T1,T2>& lhs, pair<T1,T2>& rhs );
我还想知道我是否应该为自定义 类、
编写自己的专业化
或者简单地依赖于模板版本。
So what it is gained from specializing std::pair?
性能。通用交换通常足够好(自 C++11 起),但很少是最优的(对于 std::pair
和大多数其他数据结构)。
I'm also left wondering if I should be writing my own specializations for custom classes, or simply relying on the template version.
我建议默认依赖模板,但如果分析显示它是一个瓶颈,请知道可能还有改进的余地。过早的优化等等...
大概这是出于性能原因,因为 pair
包含的类型交换成本低但复制成本高,例如 vector
。由于它可以在 first
和 second
上调用交换而不是使用临时对象进行复制,因此它可以显着提高程序性能。
std::swap
是按照以下代码行实现的:
template<typename T> void swap(T& t1, T& t2) {
T temp = std::move(t1);
t1 = std::move(t2);
t2 = std::move(temp);
}
(有关详细信息,请参阅 "How does the standard library implement std::swap?"。)
So what it is gained from specializing
std::pair
?
std::swap
可以通过以下方式特化 (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);
}
如您所见,swap
是使用 ADL 直接在对的元素上调用的:这允许在 first
上使用 swap
的自定义且可能更快的实现second
(这些实现可以利用元素内部结构的知识来提高性能)。
(有关详细信息,请参阅
交换两个对的最有效方法与交换两个向量的最有效方法不同。两种类型有不同的实现,不同的成员变量和不同的成员函数。
没有以这种方式 "swap" 两个对象的通用方法。
我的意思是,当然,对于可复制类型,您可以这样做:
T tmp = a;
a = b;
b = tmp;
但这太可怕了。
对于可移动类型,您可以添加一些 std::move
并防止复制,但是您 仍然 需要 "swap" 下一层的语义以便实际具有有用的移动语义。在某些时候,您需要专攻。
原因是性能,尤其是 pre c++11。
考虑类似 "Vector" 的类型。 Vector 具有三个字段:大小、容量和指向实际数据的指针。它是复制构造函数和复制赋值复制实际数据。 C++11版本还有一个移动构造函数和移动赋值窃取指针,将源对象中的指针设置为空。
专用的 Vector 交换实现可以简单地交换字段。
基于复制构造函数、复制赋值和析构函数的通用交换实现将导致数据复制和动态内存allocation/deallocation。
基于移动构造函数、移动赋值和析构函数的通用交换实现将避免任何数据复制或内存分配,但它会留下一些冗余的空化和 null-checks 优化器可能会也可能不会优化掉。
那么为什么要为 "Pair" 提供专门的交换实现呢?对于一对 int 和 char 没有必要。它们是普通的旧数据类型,因此通用交换就可以了。
但是如果我有一对 Vector 和 String 呢?我想对这些类型使用专门的交换操作,因此我需要对通过交换它的组件元素来处理它的对类型进行交换操作。
有一条规则(我认为它来自 Herb Sutter 的 Exceptional C++ 或 Scott Meyer 的 Effective C++ 系列),如果您的类型可以提供不抛出或比泛型更快的交换实现 std::swap
函数,它应该作为成员函数 void swap(T &other)
.
理论上,通用 std::swap()
函数可以使用模板魔术来检测 member-swap 的存在并调用它而不是执行
T tmp = std::move(lhs);
lhs = std::move(rhs);
rhs = std::move(tmp);
但是 no-one 似乎已经考虑过那个,所以人们倾向于添加免费 swap
的重载以便调用(可能更快)member-swap。