Visual Studio 2017 是否需要显式移动构造函数声明?

Does Visual Studio 2017 need an explicit move constructor declaration?(Visual Studio 2017 是否需要显式移动构造函数声明?)

本文介绍了Visual Studio 2017 是否需要显式移动构造函数声明?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

使用 Visual Studio 2015 可以成功编译以下代码,但使用 Visual Studio 2017 编译失败.Visual Studio 2017 报告:

The below code can be compiled successfully using Visual Studio 2015, but it failed using Visual Studio 2017. Visual Studio 2017 reports:

错误 C2280:std::pair::pair(const std::pair &)":试图引用已删除的函数

error C2280: "std::pair::pair(const std::pair &)": attempting to reference a deleted function

代码

#include <unordered_map>
#include <memory>

struct Node
{
  std::unordered_map<int, std::unique_ptr<int>> map_;
  // Uncommenting the following two lines will pass Visual Studio 2017 compilation
  //Node(Node&& o) = default;
  //Node() = default;
};

int main()
{
  std::vector<Node> vec;
  Node node;
  vec.push_back(std::move(node));
  return 0;
}

看起来 Visual Studio 2017 显式需要移动构造函数声明.是什么原因?

It looks like Visual Studio 2017 explicit needs a move constructor declaration. What is the reason?

推荐答案

我们看下std::vector源码(我换成了pointer_Ty 与实际类型):

Let's look at the std::vector source code (I replaced pointer and _Ty with actual types):

void _Umove_if_noexcept1(Node* First, Node* Last, Node* Dest, true_type)
    {   // move [First, Last) to raw Dest, using allocator
    _Uninitialized_move(First, Last, Dest, this->_Getal());
    }

void _Umove_if_noexcept1(Node* First, Node* Last, Node* Dest, false_type)
{   // copy [First, Last) to raw Dest, using allocator
    _Uninitialized_copy(First, Last, Dest, this->_Getal());
}

void _Umove_if_noexcept(Node* First, Node* Last, Node* Dest)
{   // move_if_noexcept [First, Last) to raw Dest, using allocator
    _Umove_if_noexcept1(First, Last, Dest,
        bool_constant<disjunction_v<is_nothrow_move_constructible<Node>, negation<is_copy_constructible<Node>>>>{});
}

如果 Nodeno-throw move-constructible 或者 not copy-constructible,则调用 _Uninitialized_move,否则,调用 _Uninitialized_copy.

If Node is no-throw move-constructible or is not copy-constructible, _Uninitialized_move is called, otherwise, _Uninitialized_copy is called.

问题在于,如果您没有显式声明移动构造函数,则类型特征 std::is_copy_constructible_v 对于 Nodetrue.此声明使复制构造函数被删除.

The problem is that the type trait std::is_copy_constructible_v is true for Node if you do not declare a move constructor explicitly. This declaration makes copy-constructor deleted.

libstdc++ 以类似的方式实现 std::vector,但与 MSVC 相比,std::is_nothrow_move_constructible_vtrue,这里是 false.因此,使用了移动语义,编译器不会尝试生成复制构造函数.

libstdc++ implements std::vector in a similar way, but there std::is_nothrow_move_constructible_v<Node> is true in contrast to MSVC, where it is false. So, move semantics is used and the compiler does not try to generate the copy-constructor.

但是如果我们强制 is_nothrow_move_constructible_v 变成 false

But if we force is_nothrow_move_constructible_v to become false

struct Base {
    Base() = default;
    Base(const Base&) = default;
    Base(Base&&) noexcept(false) { }
};

struct Node : Base {
    std::unordered_map<int, std::unique_ptr<int>> map;
};

int main() {
    std::vector<Node> vec;
    vec.reserve(1);
}

同样的错误发生:

/usr/include/c++/7/ext/new_allocator.h:136:4: error: use of deleted function ‘std::pair<_T1, _T2>::pair(const std::pair<_T1, _T2>&) [with _T1 = const int; _T2 = std::unique_ptr<int>]’
  { ::new((void *)__p) _Up(std::forward<_Args>(__args)...); }
    ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

这篇关于Visual Studio 2017 是否需要显式移动构造函数声明?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持编程学习网!

本文标题为:Visual Studio 2017 是否需要显式移动构造函数声明?

基础教程推荐