Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/126.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 移动语义==自定义交换函数过时?_C++_C++11_Swap_Move Semantics_Obsolete - Fatal编程技术网

C++ 移动语义==自定义交换函数过时?

C++ 移动语义==自定义交换函数过时?,c++,c++11,swap,move-semantics,obsolete,C++,C++11,Swap,Move Semantics,Obsolete,最近,关于如何提供自己的swap功能。使用C++11,std::swap将使用std::move和move语义尽可能快地交换给定的值。当然,这仅在提供移动构造函数和移动赋值运算符(或使用“按值传递”的运算符)时有效 现在,有了这些,是否真的有必要在C++11中编写自己的swap函数?我只能想到不可移动的类型,但同样,自定义的交换通常通过某种“指针交换”(也称为移动)来工作。也许有一些参考变量?嗯……这是一个判断的问题。我通常会让std::swap完成原型代码的工作,但对于发布代码,我会编写一个自

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


现在,有了这些,是否真的有必要在C++11中编写自己的
swap
函数?我只能想到不可移动的类型,但同样,自定义的
交换
通常通过某种“指针交换”(也称为移动)来工作。也许有一些参考变量?嗯……这是一个判断的问题。我通常会让
std::swap
完成原型代码的工作,但对于发布代码,我会编写一个自定义的swap。我通常可以编写一个自定义交换,速度大约是1个移动构造+2个移动分配+1个无资源破坏的两倍。然而,人们可能希望等到
std::swap
实际证明是一个性能问题后再动手

阿尔夫·p·斯坦巴赫的更新:

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
                 );

std::swap
也不例外,即使没有移动成员。

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

按照惯例,自定义
交换不提供抛出保证。我不知道
std::swap
。我对委员会在这方面工作的印象是,这一切都是政治性的,因此,如果他们在某个地方将
duck
定义为
bug
,或者类似的政治文字游戏,我也不会感到惊讶。因此,我不会依赖于这里的任何答案,除非它提供了一个从C++0x到标准的详细的逐条引用,包括最小的细节(以确保没有bug)。

当然,您可以按照

template <class T>
void swap(T& x, T& y)
{
  T temp = std::move(x);
  x = std::move(y);
  y = std::move(temp);
}
它不必运行构造函数和析构函数,只需交换指针(很可能实现为XCHG或类似的东西)


当然,在第一个示例中,编译器可能会优化掉构造函数/析构函数调用,但如果它们有副作用(即调用new/delete),则可能没有足够的智能来优化它们。

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

然后,
std::swap
将导致删除空指针3次(对于移动赋值运算符和统一赋值运算符情况)。编译器可能无法优化此类
删除操作,请参阅

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

更新

看起来Intel和Clang编译器能够优化空指针的删除,而GCC则不行。有关详细信息,请参阅

更新

似乎有了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_; }

谢谢你的更新,霍华德。我猜
A(const A&)不例外是一个打字错误,或者可能是吞下了符号?也就是说,你的意思是
A(A&&)noexcept(分配操作员同上)?干杯,阿尔夫:不;他给出了一个例子,其中
std::swap
即使没有实际的移动构造/赋值,也将
noexcept(true)
。@Dennis:我想我需要更多的解释,因为对我来说,它看起来像一个普通的(非移动)复制构造函数和一个普通的复制赋值运算符,据我所知,那些与
无关的是可构造的还是可分配的?@Alf:
是可构造的还是可构造的。复制构造函数的传统
T(const T&)
版本可以很好地接受r值引用,因此
T
将报告为可移动构造,即使实际移动没有发生。从
is_nothrow\u move_assignable
is_nothrow\u assignable
执行了类似的映射,因此相同的参数适用于此。@丹尼斯:谢谢大家的解释,你完全正确。我现在要回到这一点。如果您的移动构造函数/赋值运算符的副作用没有反映在您的自定义交换中,我怀疑您的实现是错误的。std::array是可复制的,但没有移动构造函数,如果这是您的意思的话。一个不可移动的东西:
来自Win32 API的关键部分
看起来像一个。它是一个不透明类型,必须初始化,不能复制,并且内部有一个指向自身的指针。一个可移动的包装器必须有一个指针来管理它。
void swap(A& x, A& y)
{
  using std::swap;
  swap(x.ptr, y.ptr);
}
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); }
 // X& operator=(X&& rhs) noexcept { if (i_) delete i_; i_ = rhs.i_;
 //                                  rhs.i_ = nullptr; return *this; }
    ~X() { if (i_) delete i_; }