c++ - 关于根据 move 赋值和 move 构造函数实现 std::swap

标签 c++ stl c++11 move-semantics

这里是 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) 保证没有效果并且
  2. std::swap 可以如上实现。

在我看来,以下引述暗示这些信念是矛盾的。

17.6.4.9 函数参数 [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]

(感谢 Howard Hinnantproviding the quote )

v 是从标准模板库中获取的某种可 move 类型的对象,并考虑调用 std::swap(v, v)。在上面的 a = std::move(b); 行中,T::operator=(T&& t) 中的情况是 this == &b,因此该参数不是唯一引用。这违反了上述要求,因此 a = std::move(b) 行在从 std::swap(v, v) 调用时调用未定义的行为>.

这里的解释是什么?

最佳答案

[res.on.arguments] 是关于客户端应该如何使用 std::lib 的声明。当客户端向 std::lib 函数发送一个 xvalue 时,客户端必须愿意假装 xvalue 实际上是一个纯右值,并期望 std::lib 能够利用它。

然而,当客户端调用 std::swap(x, x) 时,客户端不会将 xvalue 发送到 std::lib 函数。相反,是实现这样做了。因此,有责任让 std::swap(x, x) 正常工作。

也就是说,std 已经给了实现者一个保证:X 应该满足 MoveAssignable。即使处于移出状态,客户端也必须确保 X 是 MoveAssignable。此外,std::swap 的实现并不关心自 move 赋值的作用,只要它不是 X 的未定义行为即可。只要它不崩溃。

a = std::move(b);

当 &a == &b 时,此赋值的源和目标都有一个未指定(移出)的值。这可以是空操作,也可以做其他事情。只要它不崩溃,std::swap 就会正常工作。这是因为在下一行:

b = std::move(tmp);

从上一行进入 a 的任何值都将被赋予一个来自 tmp 的新值。而tmp 的原始值为a。所以除了消耗大量的 CPU 周期之外,swap(a, a) 是一个空操作。

更新

latest working draft, N4618已修改为在 MoveAssignable 中明确声明需要表达式:

t = rv

(其中 rv 是一个右值),如果 t rv 不引用同一个对象。无论如何,rv 的状态在赋值后是未指定的。还有一个附加说明需要进一步说明:

rv must still meet the requirements of the library component that is using it, whether or not t and rv refer to the same object.

关于c++ - 关于根据 move 赋值和 move 构造函数实现 std::swap,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13129031/

相关文章:

c++ - 强制复制省略?海湾合作委员会 5.4.1

c++ - 为什么 CMake 在 Ubuntu 18.04 上找不到我的 Boost 库?

c++ - STL算法函数名解析

c++ - 通过 && 在 std::vector push_back() 和 std::map operator[] 中获取参数

c++ - 在 STL 容器包装器中定义迭代器类型

c++ - 如何用强盗转换这个类型列表?

c++ - 如何在C++中制作无限序列

c++ - 从套接字读取一个字节与多个字节

c++ - CMake项目结构: How do I properly merge libraries together and include them in multiple executables

c++ - 编写 LinkedList 析构函数?