iter_swap()与掉期() - 有什么区别?(iter_swap() versus swap(

2019-07-17 13:22发布

MSDN说 :

swap应优先用于iter_swap ,将其包含在C ++标准向后兼容。

但comp.std.c ++说 :

大多数STL算法在迭代范围内工作。 因此,有意义的使用iter_swap交换那些范围内的元件的情况下,因为这是它的预期目的---交换元件由两个迭代指向。 这允许优化用于基于节点的序列,例如std::list ,由此,节点只是重新链接,而不是实际被交换的数据。

那么,哪一个是正确的? 我应该使用iter_swap ,或者我应该使用swap ? (是iter_swap为了向后兼容?)为什么?

Answer 1:

该标准本身已很少提到的iter_swap

  • 它应具有的作用swap(*a, *b)虽然没有规定它必须执行的方式。
  • 解除引用值*a*b必须是“可交换”,这意味着swap(*a, *b)必须是有效的,并因此解除引用类型必须是相同的,尽管迭代器类型不必须如此。
  • iter_swap需要在执行中使用std::reverse 。 没有这样的要求放在任何其他算法,因此这似乎是一个怪胎。

借用什么sehe发现了来自SGI文档

严格地说, iter_swap是多余的。 它的存在只是出于技术原因:在某些情况下,一些编译器有困难进行解释所需的类型推演swap(*a, *b)

所有这些似乎表明,它是对过去的一个假象。



Answer 2:

这似乎是在互联网产生的相互矛盾的信息的主机的情况之一。

  • cplusplus.com说iter_swap是相同的swap ,并通过这一逻辑,MSDN就在说,一个人应该简单地坚持是正确的swap

  • cppreference.com告诉我们,调用swap仅仅是一个可能的实现 iter_swap ,开启了可能的优化门在iter_swap某些特例,只要功能的不断复杂性担保维持原判。

该标准,下[C++11: 25.3.3/5]只说iter_swap(a,b)具有这样的结果swap(*a,*b)以及要求“ ab应为可提领”,而“ *a应交换与*b ”),这将乍一看与MSDN的解释相关。

不过,我相信微软忽略了考虑的,如果规则,它应该允许一个实现,使iter_swap的速度比swap在某些情况下(如链表的元素)。

因此,我相信, comp.std.c++报价是技术上更精确的两个。

话虽这么说,对可能进行的最优化一个相当严格的限制。 举个例子,一个实施iter_swap在链表的元素,简单地重新链接的节点,而不是物理交换的元素值-这不是一个有效的执行,因为要求,即iter_swap的观察行为匹配swap的被侵犯。

因此,我建议, 在实践中可以有一点,如果到宁愿任何好处iter_swapswap ,我建议你坚持后者的简单性和一致性。 C ++ 11移动语义应该让swap在许多情况下不在话下反正。



Answer 3:

是的,他们都做同样的事情, 如果使用得当 。 不, 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。



Answer 4:

你已经打在关键区别。

swap(*a, *b)是一个全球性的功能重新设置所有的指针指向*a指向什么内容*b ,反之亦然。 这是老tmp = aa = bb = tmp交换。

iter_swap为修改基础物体上迭代的影响,其中他们是其一部分的结构的更有限的情况下。 如果*a*b是同一链表的一部分,它是足够的iter_swap简单地交换列表中的立场。 这是因为当你想简单排序,同时又不会使/改变外部指针到列表中的对象列表中的优势 。 如果我有一个指向user对象,如果您排序我不在乎listuser对象,我不希望我的谁是“当前”用户改变想法,所以列表排序最好不要使用swap



Answer 5:

你应该使用哪一个? 取决于你使用的是它什么。 由于掉期只适用于物体,交换两个独立的整数或字符串或双打。 但iter_swap行之有效的数组和列表,在其中您可以作为证明两份不同名单换号cplusplus.com



Answer 6:

仔细阅读法律:


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(ForwardIterator1一个,ForwardIterator2 B);

    5种效果:交换(* A,* B)。

    6需要:a和b应为可提领。 *一应交换与* B。 (17.6.3.2)


因此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。



文章来源: iter_swap() versus swap() — what's the difference?
标签: c++ swap