在查看std::swap
的文档时,我看到很多专业化
看起来每个STL容器以及许多其他标准设施都有专门的交换
我认为借助模板,我们不需要所有这些专业化?
例如,
如果我自己编写var currentOrigin : Point {
return Point(self.origin.x,self.origin.y)
}
,它可以使用模板化版本正确运行:
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
我也想知道我是否应该为自定义类编写自己的专业,
或者只是依赖于模板版本。
答案 0 :(得分:38)
那么从专业化std :: pair中获得了什么?
性能。通用交换通常足够好(从C ++ 11开始),但很少是最优的(对于std::pair
,对于大多数其他数据结构而言)。
我也想知道我是否应该为自定义类编写自己的专业,或者只是依赖于模板版本。
我建议默认依赖模板,但如果分析显示它是瓶颈,请知道可能还有改进的余地。过早优化和所有......
答案 1 :(得分:33)
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?"。)
那么从专业化
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);
}
如您所见,使用ADL直接在对的元素上调用swap
:这允许在swap
和{{first
上使用second
的自定义且可能更快的实现1}} (这些实现可以利用元素内部结构的知识来获得更高的性能)。
(有关详细信息,请参阅"How does using std::swap
enable ADL?"。)
答案 2 :(得分:12)
据推测,出于性能原因,pair
包含的类型交换成本低,但复制成本高,如vector
。由于它可以调用first
和second
上的交换,而不是使用临时对象进行复制,因此可以显着提高程序性能。
答案 3 :(得分:4)
交换两对的最有效方法与交换两个向量的最有效方法不同。这两种类型具有不同的实现,不同的成员变量和不同的成员函数。
没有通用的方式以这种方式“交换”两个对象。
我的意思是,当然,对于可复制类型,你可以这样做:
T tmp = a;
a = b;
b = tmp;
但这太可怕了。
对于可移动类型,您可以添加一些std::move
并阻止副本,但是仍然需要在下一层向下“交换”语义,以便实际具有有用的移动语义。在某些时候,你需要专门化。
答案 4 :(得分:4)
原因在于性能,特别是pre c ++ 11。
考虑像&#34; Vector&#34;类型。 Vector有三个字段:大小,容量和指向实际数据的指针。它的复制构造函数和复制赋值复制实际数据。 C ++ 11版本还有一个移动构造函数和移动赋值,用于窃取指针,将源对象中的指针设置为null。
专用的Vector交换实现可以简单地交换字段。
基于复制构造函数,复制赋值和析构函数的通用交换实现将导致数据复制和动态内存分配/释放。
基于移动构造函数,移动赋值和析构函数的通用交换实现将避免任何数据复制或内存分配,但它会留下一些冗余的归零和空值检查,优化器可能会或可能无法优化掉。 / p>
那么为什么要为&#34; Pair&#34;?进行专门的交换实施?对于一对int和char,没有必要。它们是普通的旧数据类型,因此通用交换就可以了。
但是,如果我有一对说Vector和String怎么办?我想对这些类型使用专家交换操作,所以我需要在对类型上进行交换操作,通过交换它的组件元素来处理它。
答案 5 :(得分:1)
有一条规则(我认为它来自Herb Sutter的特殊C ++或Scott Meyer的有效C ++系列)如果您的类型可以提供不抛出或更快的交换实现与通用std::swap
函数相比,它应该作为成员函数void swap(T &other)
。
理论上,通用std::swap()
函数可以使用模板魔法来检测成员交换的存在并调用而不是执行
T tmp = std::move(lhs);
lhs = std::move(rhs);
rhs = std::move(tmp);
但似乎没有人想过那个,但是,所以人们倾向于添加免费swap
的重载,以便调用(可能更快)成员交换。