【问题标题】:Move semantics == custom swap function obsolete?移动语义 == 自定义交换功能已过时?
【发布时间】:2011-09-18 23:19:31
【问题描述】:

最近,manyquestionspop up关于如何提供自己的swap功能。在 C++11 中,std::swap 将使用 std::move 并移动语义以尽可能快地交换给定值。当然,这仅在您提供移动构造函数和移动赋值运算符(或使用按值传递的运算符)时才有效。

现在,鉴于此,是否真的有必要在 C++11 中编写自己的 swap 函数?我只能想到不可移动的类型,但话又说回来,自定义 swaps 通常通过某种“指针交换”(又名移动)来工作。也许有某些参考变量?嗯……

【问题讨论】:

    标签: c++ c++11 swap move-semantics obsolete


    【解决方案1】:

    这是一个判断的问题。我通常会让std::swap 完成原型代码的工作,但为发布代码编写自定义交换。我通常可以编写一个自定义交换,其速度大约是 1 次移动构建 + 2 次移动分配 + 1 次无资源破坏的两倍。但是,您可能希望等到 std::swap 被证明是性能问题后再去打扰。

    Alf P. Steinbach 更新:

    20.2.2 [utility.swap] 指定std::swap(T&, T&) 有一个noexcept 等价于:

    template <class T>
    void
    swap(T& a, T& b) noexcept
                     (
                        is_nothrow_move_constructible<T>::value &&
                        is_nothrow_move_assignable<T>::value
                     );
    

    即如果T 上的移动操作是noexcept,则T 上的std::swapnoexcept

    请注意,此规范不需要移动成员。它只要求存在来自右值的构造和赋值,如果是noexcept,那么swap就是noexcept。例如:

    class A
    {
    public:
        A(const A&) noexcept;
        A& operator=(const A&) noexcept;
    };
    

    std::swap&lt;A&gt; 是 noexcept,即使没有移动成员。

    【讨论】:

    • 感谢您的更新,霍华德。我猜A(const A&amp;) noexcept; 是一个错字,或者可能是吞下&符号? IE。你的意思是A(A&amp;&amp;) noexcept;(对于分配操作员也是如此)?干杯,
    • @Alf:不;他举了一个例子,即使没有实际的移动构造/分配,std::swap 也将是 noexcept(true)
    • @Dennis:我想我需要更多的解释,因为对我来说这看起来像一个普通的(不移动)复制构造函数和一个普通的复制赋值运算符,据我所知,它们没有什么可做的使用is_nothrow_move_constructibleis_nothrow_move_assignable
    • @Alf: is_nothrow_move_constructible&lt;T&gt;is_nothrow_constructible&lt;T, T&amp;&amp;&gt; 相同。复制构造函数的传统T(const T&amp;) 版本可以很好地接受r 值引用,因此T 将报告为可移动构造,即使没有发生实际移动。从is_nothrow_move_assignable&lt;T&gt;is_nothrow_assignable&lt;T, T&amp;&amp;&gt; 执行了类似的映射,所以同样的论点也适用。
    • @Dennis:谢谢大家的解释,你说的很对。我现在才回到这个话题。
    【解决方案2】:

    当然,您可以将交换实现为

    template <class T>
    void swap(T& x, T& y)
    {
      T temp = std::move(x);
      x = std::move(y);
      y = std::move(temp);
    }
    

    但我们可能有自己的类,比如A,我们可以更快地交换它。

    void swap(A& x, A& y)
    {
      using std::swap;
      swap(x.ptr, y.ptr);
    }
    

    无需运行构造函数和析构函数,只需交换指针(很可能实现为 XCHG 或类似的东西)。

    当然,编译器可能会优化第一个示例中的构造函数/析构函数调用,但如果它们有副作用(即调用 new/delete),它可能不够聪明,无法将它们优化掉。

    【讨论】:

    • 如果您的移动构造函数/赋值运算符的副作用未反映在您的自定义交换中,我怀疑您的实现错误。
    【解决方案3】:

    可能有一些类型可以交换但不能移动。我不知道任何不可移动的类型,所以我没有任何例子。

    【讨论】:

    • std::array 是可复制的,但没有移动构造函数,如果这就是你的意思的话。
    • 一个不可移动的东西:来自 Win32 API 的CRITICAL_SECTION 看起来像一个。它是一个不透明的类型,它必须被初始化,它不能被复制,它内部有一个指向自身的指针。围绕一个的可移动包装器必须有一个指针来管理它。
    【解决方案4】:

    按照惯例,自定义swap 提供不抛出保证。我不知道std::swap。我对委员会工作的印象是这都是政治性的,所以如果他们在某个地方将duck 定义为bug 或类似的政治文字游戏策略,我不会感到惊讶。所以我不会依赖这里的任何答案,除非它通过引用 C++0x 的标准来提供详细的打击,直到最小的细节(以确保没有 @987654325 @)。

    【讨论】:

      【解决方案5】:

      考虑以下持有内存分配资源的类(为简单起见,用单个整数表示):

      class X {
          int* i_;
      public:
          X(int i) : i_(new int(i)) { }
          X(X&& rhs) noexcept : i_(rhs.i_) { rhs.i_ = nullptr; }
       // X& operator=(X&& rhs) noexcept { delete i_; i_ = rhs.i_;
       //                                  rhs.i_ = nullptr; return *this; }
          X& operator=(X rhs) noexcept { swap(rhs); return *this; }
          ~X() { delete i_; }
          void swap(X& rhs) noexcept { std::swap(i_, rhs.i_); }
      };
      
      void swap(X& lhs, X& rhs) { lhs.swap(rhs); }
      

      然后std::swap 导致删除空指针 3 次(对于 移动赋值运算符统一赋值运算符情况)。编译器可能无法优化出这样的delete,请参阅https://godbolt.org/g/E84ud4

      自定义swap 不会调用任何delete,因此可能更有效。我想这就是std::unique_ptr 提供自定义std::swap 专业化的原因。

      更新

      似乎 Intel 和 Clang 编译器能够优化空指针的删除,但 GCC 不能。详情请见Why GCC doesn't optimize out deletion of null pointers in C++?

      更新

      似乎使用 GCC,我们可以通过如下重写 X 来防止调用 delete 运算符:

       // X& operator=(X&& rhs) noexcept { if (i_) delete i_; i_ = rhs.i_;
       //                                  rhs.i_ = nullptr; return *this; }
          ~X() { if (i_) delete i_; }
      

      【讨论】:

        猜你喜欢
        • 2021-04-26
        • 1970-01-01
        • 1970-01-01
        • 2013-05-04
        • 1970-01-01
        • 2018-03-13
        • 2019-09-28
        • 2014-10-14
        • 1970-01-01
        相关资源
        最近更新 更多