std::swap 在 VS 2013 中导致无限递归

std::swap causes infinite recursion in VS 2013

本文关键字:递归 无限 2013 swap VS std      更新时间:2023-10-16

请考虑以下代码片段:

#include <utility>
struct foo
{
    foo()
    {
    }
    foo(foo&& other)
    {
        std::swap(*this, other);
    }
};
int main(int argc, char** argv)
{
    foo f(std::move(foo()));
}

它在VS2013中导致无限递归,但无法使用g ++/clang进行编译,并出现删除operator=的错误(这是有道理的):

In file included from /usr/local/include/c++/5.3.0/bits/stl_pair.h:59:0,
                 from /usr/local/include/c++/5.3.0/utility:70,
                 from main.cpp:1:
/usr/local/include/c++/5.3.0/bits/move.h: In instantiation of 'void std::swap(_Tp&, _Tp&) [with _Tp = foo]':
main.cpp:11:26:   required from here
/usr/local/include/c++/5.3.0/bits/move.h:186:11: error: use of deleted function 'foo& foo::operator=(const foo&)'
       __a = _GLIBCXX_MOVE(__b);
           ^
main.cpp:3:12: note: 'foo& foo::operator=(const foo&)' is implicitly declared as deleted because 'foo' declares a move constructor or move assignment operator
     struct foo
            ^
In file included from /usr/local/include/c++/5.3.0/bits/stl_pair.h:59:0,
                 from /usr/local/include/c++/5.3.0/utility:70,
                 from main.cpp:1:
/usr/local/include/c++/5.3.0/bits/move.h:187:11: error: use of deleted function 'foo& foo::operator=(const foo&)'
       __b = _GLIBCXX_MOVE(__tmp);
           ^

我认为这是一个VS错误。但是,该标准是否对如何实施std::swap施加了任何规则?我的意思是这仅仅是 VS 中的错误实现还是违反标准?

是的,这段代码不应该编译,因为根据 C++14 标准草案 N4140 [utility.swap]/2:

要求:T型应为MoveConstructible型(表 20)和MoveAssignable型(表 22)。

C++11 包含相同的要求。

所以这确实是VS2013中的一个错误,实际上在VS2015中已经修复。

但是,在添加如下所示的移动分配运算符后:

foo& operator=(foo&& other) = default;

您将在GCC,Clang和VS2015(DEMO)中获得无限递归。

人们可以很容易地猜到,由于swap()需要MoveConstructibleMoveAssignable,它在内部使用移动构造函数和/或移动赋值运算符,因此通过调用this和参数的swap()来实现它们绝对是非法的。