在移动分配和移动构造函数方面实现std :: swap [英] On implementing std::swap in terms of move assignment and move constructor

查看:135
本文介绍了在移动分配和移动构造函数方面实现std :: swap的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这是 std :: swap 的可能定义:

  template< class T> 
void swap(T& a,T& b){
T tmp(std :: move(a));
a = std :: move(b);
b = std :: move(tmp);
}

我相信


  1. std :: swap(v,v)确保没有效果,


下面的引用似乎可以用来实现上面的例子:code> std :: swap


17.6.4.9函数参数[res.on.arguments] b


1除非另有说明,否则以下每个适用于C ++标准库中定义的
的所有参数。



...




  • 如果函数参数绑定到右值引用参数,则实现可以假定此参数是对
    这个参数的唯一引用。 [注意:如果参数是
    形式的通用参数T&&并且类型A的左值被绑定,该参数绑定到
    左值引用(14.8.2.1),因此不被先前的
    句子覆盖。 - end note] [注意:如果程序将一个左值传递给
    xvalue,同时将该值传递给库函数(例如通过
    调用参数move(x)的函数),程序
    有效地要求该函数将该左值作为临时对象。
    实现可以自由地优化掉别名检查,如果参数是一个左值,那么
    可能是需要的。 -endnote]


(感谢 Howard Hinnant 提供报价



v 从标准模板库并考虑调用 std :: swap(v,v)。在 a = std :: move(b); 行中, T :: operator =(T& ) this ==& b ,因此参数是唯一引用。这违反了上面的要求,因此 a = std :: move(b)行调用未定义的行为时从 std ::



< >解决方案

[res.on.arguments]是关于客户端应如何使用std :: lib的语句。当客户端向std :: lib函数发送xvalue时,客户端必须愿意假设xvalue实际上是prvalue,并且期望std :: lib利用它。



然而,当客户端调用std :: swap(x,x)时,客户端不会向std :: lib函数发送xvalue。正是这样做的实现。因此,onus是在执行std :: swap(x,x)工作。



这就是说,std给了实现者一个保证:X应满足 MoveAssignable 。即使在移出状态下,客户端必须确保X是MoveAssignable。此外, std :: swap 的实现并不关心什么是自我移动赋值,只要它不是X的未定义的行为。只要不会崩溃。

  a = std :: move(b); 

当& a ==& b时,此作业的来源和目标都有未指定(移动)值。这可以是一个无操作,或者它可以做别的事情。只要它不崩溃,std :: swap将正常工作。这是因为在下一行:

  b = std :: move(tmp); 

无论什么值进入 a 线将被赋予一个新的值从 tmp tmp 的原始值为 a 。所以除了烧掉很多cpu周期, swap(a,a)是无操作。


Here is a possible definition of std::swap:

template<class T>
void swap(T& a, T& b) {
  T tmp(std::move(a));
  a = std::move(b);
  b = std::move(tmp);
}

I believe that

  1. std::swap(v,v) is guaranteed to have no effects and
  2. std::swap can be implemented as above.

The following quote seems to me to imply that these beliefs are contradictory.

17.6.4.9 Function arguments [res.on.arguments]

1 Each of the following applies to all arguments to functions defined in the C++ standard library, unless explicitly stated otherwise.

...

  • If a function argument binds to an rvalue reference parameter, the implementation may assume that this parameter is a unique reference to this argument. [ Note: If the parameter is a generic parameter of the form T&& and an lvalue of type A is bound, the argument binds to an lvalue reference (14.8.2.1) and thus is not covered by the previous sentence. — end note ] [ Note: If a program casts an lvalue to an xvalue while passing that lvalue to a library function (e.g. by calling the function with the argument move(x)), the program is effectively asking that function to treat that lvalue as a temporary. The implementation is free to optimize away aliasing checks which might be needed if the argument was an lvalue. —endnote]

(thanks to Howard Hinnant for providing the quote)

Let v be an object of some movable type taken from the Standard Template Library and consider the call std::swap(v, v). In the line a = std::move(b); above, it is the case inside T::operator=(T&& t) that this == &b, so the parameter is not a unique reference. That is a violation of the requirement made above, so the line a = std::move(b) invokes undefined behavior when called from std::swap(v, v).

What is the explanation here?

解决方案

[res.on.arguments] is a statement about how the client should use the std::lib. When the client sends an xvalue to a std::lib function, the client has to be willing to pretend that the xvalue is really a prvalue, and expect the std::lib to take advantage of that.

However when the client calls std::swap(x, x), the client isn't sending an xvalue to a std::lib function. It is the implementation that is doing so instead. And so the onus is on the implementation to make std::swap(x, x) work.

That being said, the std has given the implementor a guarantee: X shall satisfy MoveAssignable. Even if in a moved-from state, the client must ensure that X is MoveAssignable. Furthermore, the implementation of std::swap doesn't really care what self-move-assignment does, as long as it is not undefined behavior for X. I.e. as long as it doesn't crash.

a = std::move(b);

When &a == &b, both the source and target of this assignment have an unspecified (moved-from) value. This can be a no-op, or it can do something else. As long as it doesn't crash, std::swap will work correctly. This is because in the next line:

b = std::move(tmp);

Whatever value went into a from the previous line is going to be given a new value from tmp. And tmp has the original value of a. So besides burning up a lot of cpu cycles, swap(a, a) is a no-op.

这篇关于在移动分配和移动构造函数方面实现std :: swap的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆