我有一个带有已删除移动构造函数的类,当我尝试在 MSVC(v.15.8.7 Visual C++ 2017)中调用 std::vector::push_back() 时,我收到一条错误消息,提示我正在尝试访问已删除的移动构造函数。但是,如果我定义了移动构造函数,则代码会编译,但永远不会调用移动构造函数。两个版本都可以在 gcc (v. 5.4) 上按预期编译和运行。
这是一个简化的例子:
#include <iostream>
#include <vector>
struct A
{
public:
A() { std::cout << "ctor-dflt" << std::endl; }
A(const A&) { std::cout << "ctor-copy" << std::endl; }
A& operator=(const A&) { std::cout << "asgn-copy" << std::endl; return *this; }
A(A&&) = delete;
A& operator=(A&& other) = delete;
~A() { std::cout << "dtor" << std::endl; }
};
int main()
{
std::vector<A> v{};
A a;
v.push_back(a);
}
在 Visual Studio 上编译时会出现以下错误:
error C2280: 'A::A(A &&)': attempting to reference a deleted function
如果我定义移动构造函数而不是删除它
A(A&&) { std::cout << "ctor-move" << std::endl; }
一切都编译并运行,输出如下:
ctor-dflt
ctor-copy
dtor
dtor
正如预期的那样。不调用移动构造函数。 (直播代码:https://rextester.com/XWWA51341)
此外,这两个版本都可以在 gcc 上完美运行。 (直播代码:https://rextester.com/FMQERO10656)
所以我的问题是,为什么对不可移动对象的 std::vector::push_back() 调用不能在 MSVC 中编译,即使显然从未调用过移动构造函数?
最佳答案
这是未定义的行为,所以 gcc 和 MSVC 都是正确的。
我最近在推特上发布了一个类似案例 using std::vector::emplace_back with a type that has a deleted move constructor就像这个一样,它是未定义的行为。所以这里所有的编译器都是正确的,未定义的行为不需要诊断,尽管实现可以自由地这样做。
我们可以从 [container.requirements.general] Table 88 开始了解其中的原因。这告诉我们 push_back
要求 T
是 CopyInsertable:
Requires: T shall be CopyInsertable into x
我们可以看到 CopyInsertable 需要 MoveInsertable [container.requirements#general]p15 :
T is CopyInsertable into X means that, in addition to T being MoveInsertable into X...
在这种情况下,A
不是 MoveInsertable。
我们可以通过查看 [res.on.required]p1 看到这是未定义的行为。 :
Violation of the preconditions specified in a function's Requires: paragraph results in undefined behavior unless the function's Throws: paragraph specifies throwing an exception when the precondition is violated.
[res.on.required] 属于 Library-wide requirements .
在这种情况下,我们没有 throws 段落,因此我们有未定义的行为,这不需要诊断,正如我们从它的 definition 中看到的那样:
behavior for which this International Standard imposes no requirements....
注意,这与需要诊断的格式错误非常不同,我在 my answer here 中解释了所有细节.
关于c++ - std::vector::push_back() 不能在 MSVC 上为具有已删除移动构造函数的对象编译,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52945648/