最近,many questions pop up了解了如何提供自己的swap
功能。使用C ++ 11,std::swap
将使用std::move
并移动语义以尽可能快地交换给定值。当然,这仅适用于提供移动构造函数和移动赋值运算符(或使用按值传递的运算符)。
现在,有了这个,是否真的有必要在C ++ 11中编写自己的swap
函数?我只能想到不可移动的类型,但是再一次,自定义swap
通常通过某种“指针交换”(也就是移动)来工作。也许有某些参考变量?唔...
答案 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::swap
为T
。
请注意,此规范不需要移动成员。它只需要存在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_; }