std :: swap(x,x)是否保证x不变?

Kno*_*abe 24 c++ swap c++11

这个问题是基于Scott Meyers最近发表的博客文章.

看起来"显而易见" std::swap(x, x)应该x在C++ 98和C++ 11中保持不变,但我无法在任何标准中找到任何保证.C++ 98 std::swap在复制构造和复制赋值方面进行了定义,而C++ 11则根据移动构造和移动赋值来定义它,这似乎是相关的,因为在C++ 11(和C++ 14)中,17.6 .4.9表示移动分配不需要自我分配安全:

如果函数参数绑定到右值引用参数,则实现可以假定此参数是对此参数的唯一引用.... [注意:如果程序在将左值传递给库函数时将左值转换为x值(例如通过使用参数move(x)调用函数),程序实际上要求该函数将该左值视为暂时的.实现可以自由地优化别名检查,如果参数是左值,则可能需要这些检查. - 尾注]

引起这种措辞的缺陷报告使结果清晰:

这澄清了移动赋值运算符不需要执行复制赋值运算符中常见(并且需要)的传统if(this!=&rhs)测试.

但是在C++ 11和C++ 14中,std::swap预计会使用这个实现,

template<typename T>
void swap(T& lhs, T& rhs)
{
  auto temp(std::move(lhs));
  lhs = std::move(rhs);
  rhs = std::move(temp); 
}
Run Code Online (Sandbox Code Playgroud)

并且第一个赋值是对self进行赋值,其中参数是rvalue.如果移动赋值运算符T遵循标准库的策略并且不担心赋值给自己,那么这似乎可以判断未定义的行为,这也意味着它std::swap(x, x)也会具有UB.

即使是孤立的,这也是令人担忧的,但是如果我们认为std::swap(x, x)在C++ 98中应该是安全的,那么这也意味着C++ std::swap11/14可以默默地破坏C++ 98代码.

所以std::swap(x, x)保证x保持不变?在C++ 98中?在C++ 11中?如果是,那么它如何与17.6.4.9的移动分配许可相互作用而不是自我分配安全的?

pax*_*blo 5

我相信至少在 C++20 (a)中,这可以由[utility.requirements]以下部分涵盖:

15.5.3.2描述了对可交换类型和可交换表达式的要求。

引用的部分[swappable.requirements]进一步指出(我强调最后两个要点):

一个对象t可以与另一个对象交换u当且仅当:

  • 表达式swap(t, u)swap(u, t)在下述上下文中求值时有效;和
  • 这些表达式具有以下效果:
    • 由 引用的对象t具有最初由 持有的值u;和
    • 由 引用的对象u具有最初由 持有的值t

在我看来,如果自我交换以某种方式损坏了内容,那些粗体部分将失效,这意味着它们将不可交换。

同一部分后来还指出:

t当且仅当可分别与类型的任何右值或左值t交换时,右值或左值才是可交换的。T

那里没有关于自我交换的胡言乱语,它清楚地表明了任何右值或左值(分别),包括它自己。


(a)这两个约束也存在于 C++17、c++14 和 C++11 中,任何比这更旧的东西,我并不关心:-)