最近,出现了很多 关于 如何提供自己的功能的问题。在 C++11 中,将使用和移动语义以尽可能快地交换给定值。当然,这仅在您提供移动构造函数和移动赋值运算符(或使用按值传递的运算符)时才有效。swap
std::swap
std::move
swap
现在,鉴于此,是否真的有必要在 C++11中编写自己的函数?我只能想到不可移动的类型,但话又说回来, customswap
通常通过某种“指针交换”(又名移动)来工作。也许有某些参考变量?嗯……
这是一个判断的问题。我通常会让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
,那么std::swap
是。T
noexcept
请注意,此规范不需要移动成员。它只要求存在来自右值的构造和赋值,如果存在,noexcept
则交换将是noexcept
。例如:
class A
{
public:
A(const A&) noexcept;
A& operator=(const A&) noexcept;
};
std::swap<A>
是 noexcept,即使没有移动成员。
当然,您可以将交换实现为
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),它可能不够聪明,无法将它们优化掉。
可能有一些类型可以交换但不能移动。我不知道任何不可移动的类型,所以我没有任何例子。
按照惯例,定制swap
提供不投掷保证。我不知道std::swap
。我对委员会工作的印象是,这都是政治性的,所以如果他们在某个地方定义duck
为bug
,或类似的政治文字游戏策略,我不会感到惊讶。所以我不会依赖这里的任何答案,除非它通过引用 C++0x 的标准来提供详细的打击,直到最小的细节(以确保没有bug
)。
考虑以下包含内存分配资源的类(为简单起见,由单个整数表示):
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 不能。请参阅为什么 GCC 不优化 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_; }