移动语义==自定义交换功能已过时?

时间:2011-06-20 19:30:41

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

最近,many questions pop up了解了如何提供自己的swap功能。使用C ++ 11,std::swap将使用std::move并移动语义以尽可能快地交换给定值。当然,这仅适用于提供移动构造函数和移动赋值运算符(或使用按值传递的运算符)。

现在,有了这个,是否真的有必要在C ++ 11中编写自己的swap函数?我只能想到不可移动的类型,但是再一次,自定义swap通常通过某种“指针交换”(也就是移动)来工作。也许有某些参考变量?唔...

5 个答案:

答案 0 :(得分:22)

这是一个判断问题。我通常会让std::swap完成原型代码的工作,但是对于发布代码,请编写自定义交换。我通常可以写一个自定义交换,大约是1移动构造的两倍+ 2个移动分配+ 1个无资源的破坏。然而,人们可能希望等到std::swap实际证明是性能问题之后再去打扰。

Alf P. Steinbach的更新:

20.2.2 [utility.swap]指定std::swap(T&, T&)的{​​{1}}等效于:

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上的std::swapT

请注意,此规范不需要移动成员。它只需要存在rvalues的构造和赋值,如果它是noexcept,那么swap将是noexcept。 E.g:

noexcept
即使没有移动成员,

class A { public: A(const A&) noexcept; A& operator=(const A&) noexcept; }; 也是例外。

答案 1 :(得分:1)

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

答案 2 :(得分:0)

按惯例,自定义swap提供无投掷保证。我不知道std::swap。我对委员会工作的印象是它完全是政治性的,所以如果他们某处将duck定义为bug或类似的政治文字游戏演习,我不会感到惊讶。所以我不会依赖这里的任何答案,除非它提供了一个详细的打击,从C ++ 0x引用标准到最小细节(以确保没有{{ 1}})。

答案 3 :(得分:0)

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

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),那么它们可能不够智能,无法将它们优化掉。

答案 4 :(得分:0)

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

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中,我们可以通过重写delete来阻止调用X运算符,如下所示:

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