MSDN说 :
swap
应优先用于iter_swap
,将其包含在C ++标准向后兼容。
但comp.std.c ++说 :
大多数STL算法在迭代范围内工作。 因此,有意义的使用iter_swap
交换那些范围内的元件的情况下,因为这是它的预期目的---交换元件由两个迭代指向。 这允许优化用于基于节点的序列,例如std::list
,由此,节点只是重新链接,而不是实际被交换的数据。
那么,哪一个是正确的? 我应该使用iter_swap
,或者我应该使用swap
? (是iter_swap
为了向后兼容?)为什么?
该标准本身已很少提到的iter_swap
:
- 它应具有的作用
swap(*a, *b)
虽然没有规定它必须执行的方式。 - 解除引用值
*a
和*b
必须是“可交换”,这意味着swap(*a, *b)
必须是有效的,并因此解除引用类型必须是相同的,尽管迭代器类型不必须如此。 -
iter_swap
需要在执行中使用std::reverse
。 没有这样的要求放在任何其他算法,因此这似乎是一个怪胎。
借用什么sehe发现了来自SGI文档 :
严格地说, iter_swap
是多余的。 它的存在只是出于技术原因:在某些情况下,一些编译器有困难进行解释所需的类型推演swap(*a, *b)
所有这些似乎表明,它是对过去的一个假象。
这似乎是在互联网产生的相互矛盾的信息的主机的情况之一。
该标准,下[C++11: 25.3.3/5]
只说iter_swap(a,b)
具有这样的结果swap(*a,*b)
以及要求“ a
和b
应为可提领”,而“ *a
应交换与*b
”),这将乍一看与MSDN的解释相关。
不过,我相信微软忽略了考虑的,如果规则,它应该允许一个实现,使iter_swap
的速度比swap
在某些情况下(如链表的元素)。
因此,我相信, comp.std.c++
报价是技术上更精确的两个。
话虽这么说,对可能进行的最优化一个相当严格的限制。 举个例子,一个实施iter_swap
在链表的元素,简单地重新链接的节点,而不是物理交换的元素值-这不是一个有效的执行,因为要求,即iter_swap
的观察行为匹配swap
的被侵犯。
因此,我建议, 在实践中可以有一点,如果到宁愿任何好处iter_swap
过swap
,我建议你坚持后者的简单性和一致性。 C ++ 11移动语义应该让swap
在许多情况下不在话下反正。
是的,他们都做同样的事情, 如果使用得当 。 不, std::iter_swap
不会被弃用(由被放置在标准的兼容性 §D 功能部分)。 MSDN的报价是误导不屑一顾。 问题是,它的不方便使用std::swap
正常。
您应该使用iter_swap
,原因很简单,这是一个更高的抽象。
swap
通常重载用户定义的类型。 调用它的正确方法是
using std::swap;
swap( blah, bleh );
不单
std::swap( blah, bleh );
这是躲藏在§17.6.3.2,特别¶3:
在其中的上下文swap(t, u)
和swap(u, t)
被评估应确保通过重载解析(13.3)上的候选集合,其中包括选择命名为“交换”的二进制非成员函数:
-在两个swap
中所定义的函数模板<utility>
(20.2)和
- 由参数相关的查找(3.4.2)中产生的查找集。
iter_swap
是不是这样一个特殊的重载名,并自定义其功能需要添加模板专业化namespace std {}
因此, iter_swap
有效地封装可插拔接口,否则你将实现每一次的一部分。
它实际上是一个更友好的界面,无论是否有是有史以来为您实现其特定参数的语义差别。 (而不是它是潜在的优化应该被忽视。MSDN可能会给他们的意见,但他们无法预料会发生什么库作者可以提供使用“向后兼容的接口。”)
至于的特殊化iter_swap
与来自观察地不同的结果swap( *a, *b )
,这似乎是不符合标准与要求§25.3.3/ 5,
功效: swap(*a, *b)
你举的例子听起来像是一个可观察的区别,因为指针*a
和*b
前和术后都有效。 这是遗憾的是在库实现的bug。
你已经打在关键区别。
swap(*a, *b)
是一个全球性的功能重新设置所有的指针指向*a
指向什么内容*b
,反之亦然。 这是老tmp = a
, a = b
, b = tmp
交换。
iter_swap
为修改基础物体上迭代的影响,其中他们是其一部分的结构的更有限的情况下。 如果*a
和*b
是同一链表的一部分,它是足够的iter_swap
简单地交换列表中的立场。 这是因为当你想简单排序,同时又不会使/改变外部指针到列表中的对象列表中的优势 。 如果我有一个指向user
对象,如果您排序我不在乎list
的user
对象,我不希望我的谁是“当前”用户改变想法,所以列表排序最好不要使用swap
。
你应该使用哪一个? 取决于你使用的是它什么。 由于掉期只适用于物体,交换两个独立的整数或字符串或双打。 但iter_swap行之有效的数组和列表,在其中您可以作为证明两份不同名单换号cplusplus.com
仔细阅读法律:
20.2.2交换[utility.swap]
模板空隙交换(T&A,T和b)noexcept(is_nothrow_move_constructible ::值&& is_nothrow_move_assignable ::值);
2要求:T型应MoveConstructible和MoveAssignable。 (表20)和(表22)
3种效果:存储在两个位置中的交流值。
模板空隙交换(T(&一)[N],T(和b)[N])noexcept(noexcept(交换(* A,* B)));
4需要:A [1]应拔插用b [I]所有在范围[0,N)1。 (17.6.3.2)
5种效果:swap_ranges(A,A + N,b)中
25.3.3交换[alg.swap]
因此iter_swap需要交换存储在两个解除引用位置或解除引用位置的范围,并且任何试图交换本身是对抗一致性的参考文献中或位置的值。 明确禁止猜测优化是躺在后面的std :: iter_swap的原因。 相反,随着Potatoswatter被正确地指出,封装和抽象是其存在的主要原因。 的std :: iter_swap和std ::交换每个属于diferent抽象层,通过过载分辨率中选择的相同的方式的std ::交换本身并命名为“交换”的任何二进制非成员函数不同。
交换开发商和设计师的角色,了解达到相同的结果,并不意味着是相同的,如“即使从基本型声明的typedef是一个编译器只是噪音,它不是为读者噪音”。 把它作为一个笑话,但我们可以说整个C ++只是一个deprecatable神器包木窗C,因为两者通过包装的方式表示从另一个抽象的代码块做同样的事情,而在最低水平,等等。 特别是当线是如此之薄,如标准:: iter_swap,“交换”和std ::交换的情况下。 也许“使用的std ::交换”只有很少的caracters和消失一次编译,而是指注入的标识符,建设整个重载决策机制。 注入了个遍,建了个遍,更换了个遍,丢弃了个遍。 远从抽象,封装和回收方法。
暴露内部工作trought上层给出了和失败的维护aditional的潜在机会。 在交换域,缺失(或搞乱)一个上了深刻的元编程壳设计“使用的std ::互换”,将您的模板函数中默默等待,等待一个平凡的交换,根本的还是C-阵列类型划分构建,如果幸运的,或甚至StackOverflow的(TM)由无限递归的手段。 显然,实现一个可扩展的机制,必须要公布,但也必须得到尊重。 关于平凡拔插,有什么心事moveconstructible和moveassignable是针对其自己的类型热插拔,即使它没有一个重载的交换分辨率挂钩的,确实也有晦涩的技术来禁用不需要交换行为。
这个感觉,也许这一切都可以在标准:: iter_swap标识本身的unproper解释恢复:这并不代表“迭代交换”,而是“交换可迭代”。 不要在争论是前向迭代器的标准要求,被愚弄:在本质上,一个指针是一个随机访问迭代,从而满足要求。 Phisicallyü通过指针传递,逻辑ü通过迭代器传递。 该委员会通常会尝试以指定的最低要求的设施与定义和预期的行为,没有什么更多的工作。 在“迭代交换”的名称正确地暴露了目标和机制的权力。 该“标准:: iter_swap”标识似乎不是由于产生混乱,但为时已晚去改变它,并撤消所有的代码库依托。
随意调换为u希望,只要它的工作原理,但请不上我的手表。 混合抽象层不会让编译器哭了,但接口实在是太酷避免。 取而代之的是,这里是一个片段,以帮助在未来的指导意见:
//#include <utility> // std::swap is not required here
#include <algorithm> // std::iter_swap is
namespace linker {
class base {
};
class member {
};
template<class M = member, class B = base> // requires swappable base and member
class link : B {
public:
void swap(link &other) { // using iterable swapping
std::iter_swap(static_cast<B*>(this), static_cast<B*>(&other));
std::iter_swap(&_member, &other._member);
}
private:
M _member;
};
template<class base, class member>
void swap(link<base,member>& left, link<base,member>& right) { // extending iterable swapping
left.swap(right);
}
}
namespace processor {
template<class A, class B>
void process(A &a, B &b) { // using iterable swapping
std::iter_swap(&a, &b);
}
}
int main() {
#if !defined(PLEASE_NO_WEIRDNESS)
typedef
linker::link<
linker::link<
linker::link< int[1] >,
linker::link< void*, linker::link<> >
>[2],
linker::link<
linker::member[3]
>
>
swappable[4]; // just an array of n-ary hierarchies
#else
typedef linker::link<> swappable;
#endif
swappable a, b;
processor::process(a, b);
}
感兴趣的aditional的指导几点:
交换手段抛出的异常。 声明似乎愚蠢的,但它并不是一旦你知道交换成语并不专注于性能,但在极端的安全性和稳健性。
的std :: iter_swap展示的元编程的许多可爱的,但被忽略的功能之一:模板不仅不超载的分辨率,但也命名空间分辨率,允许其在未知和不相关的命名空间链中的首次使用。 谢谢,有一点少操心。
热插拔要求允许u到使用std ::交换直接如果(且仅当)u能得到使两个靶是基本或c-阵列以基本类型的的假设,因此允许编译器绕过任何过载的分辨率。 可悲的是排除了几乎每一个模板的参数。 使用的std ::交换直接意味着两个目标是相同类型(或被迫成为同类型)的。
不要对一类至极声明可插拔能力浪费的努力已经是本身平凡热插拔,就像我们的链接模板类(尝试删除链接::交换,行为将不会改变)。
“交换”被设计为可扩展的diferent类型的交换,
自动为同一类型。 心念一类型不是“交换”或
“非可交换的”本身,而是“交换,用”或
“非可交换的,与”其他类型。
最后,我不知道有多少读者会注意到
20.2.2交换[utility.swap]
25.3.3交换[alg.swap]
并识别实用程序不是一个算法。 在微软Dinkumware的实现,除其他外,性病:: iter_swap只是住在了错误的标题为方便起见,至极是没有错的。 也许只是它的标识符。
编辑:正面临着一些更相关的错误后,我因子评分也sumarize他们是这样的:一个算法是一个概念,所以一般和具体的,每个人大约是专注其中的一个时间,一个设计师哭别的地方。 在的std :: iter_swap的情况下,由于commitee显然没有给出自由,任何试图调整算法作为重新链接猜测应该会具有不同的含义和标识。 此外,也许有人错过容器确实有一个交换成员函数,在优化确实适用。
更好的重构使你的最后一层对象非数据,基本的,或者代表隐藏较重的物体(如果有足够重流)。 拥抱资源adquisition应该是初始化( RAII )和两个新删除过载和容器分配器有使用 ,零aditional的努力,发挥真正的交换利益。 优化资源,使U移动只在readquisition数据,然后让C ++设计你的类型的方便,安全,快捷的默认。
座右铭:早在过去,人们挣扎与太胖内存,磁盘速度太慢数据。 如今,迭代向量从存储池过滤,并在流平行管进行处理。 明天汽车将独自驾车。 值得一PostIt。