c++ - 当 "moved"对象在 union 中有一个 "non-trivial"成员时,为什么要强制复制构造函数?

标签 c++ c++14 move unions

我注意到 move 可以应用于与“非平凡”(不确切知道,但例如原始类型很好)成员。例如,以下代码编译(C++14,Clang):

#include <vector>
#include <string>

class Foo {
public:
  union {
    int i;
    bool b;
  };

  Foo() {};
  ~Foo() {};

  // Move constructor to default.
  Foo(Foo &&) = default;

  // Copy constructor deleted.
  Foo(const Foo &) = delete;
};

int main() {
  std::vector<Foo> v;
  v.push_back(Foo());
}

注意拷贝构造函数被删除了。由于 std::vectorpush_back 可以接受右值引用,因此在这种情况下它将使用它,并且不会发生 copy。但是,一旦将“非平凡”类型添加到 union 中,就会强制复制构造函数 - 因此它不会编译:

#include <vector>
#include <string>

class Foo {
public:
  union {
    int i;
    bool b;
    std::string s; // <-- Added element causing compile error.
  };

  Foo() {};
  ~Foo() {};

  // Move constructor to default.
  Foo(Foo &&) = default;

  // Copy constructor deleted.
  Foo(const Foo &) = delete;
};

int main() {
  std::vector<Foo> v;
  v.push_back(Foo());
}

编译错误信息的相关部分:

In file included from experiment/miniso.cpp:1:
In file included from /usr/bin/../lib/gcc/x86_64-linux-gnu/7.2.0/../../../../include/c++/7.2.0/vector:61:
In file included from /usr/bin/../lib/gcc/x86_64-linux-gnu/7.2.0/../../../../include/c++/7.2.0/bits/allocator.h:46:
In file included from /usr/bin/../lib/gcc/x86_64-linux-gnu/7.2.0/../../../../include/x86_64-linux-gnu/c++/7.2.0/bits/c++allocator.h:33:
/usr/bin/../lib/gcc/x86_64-linux-gnu/7.2.0/../../../../include/c++/7.2.0/ext/new_allocator.h:136:23: error: call to deleted constructor of 'Foo'
        { ::new((void *)__p) _Up(std::forward<_Args>(__args)...); }
                             ^   ~~~~~~~~~~~~~~~~~~~~~~~~~~~
/usr/bin/../lib/gcc/x86_64-linux-gnu/7.2.0/../../../../include/c++/7.2.0/bits/alloc_traits.h:475:8: note: in instantiation of function template
      specialization '__gnu_cxx::new_allocator<Foo>::construct<Foo, Foo>' requested here
        { __a.construct(__p, std::forward<_Args>(__args)...); }
              ^
/usr/bin/../lib/gcc/x86_64-linux-gnu/7.2.0/../../../../include/c++/7.2.0/bits/vector.tcc:100:21: note: in instantiation of function template
      specialization 'std::allocator_traits<std::allocator<Foo> >::construct<Foo, Foo>' requested here
            _Alloc_traits::construct(this->_M_impl, this->_M_impl._M_finish,
                           ^
/usr/bin/../lib/gcc/x86_64-linux-gnu/7.2.0/../../../../include/c++/7.2.0/bits/stl_vector.h:954:9: note: in instantiation of function template
      specialization 'std::vector<Foo, std::allocator<Foo> >::emplace_back<Foo>' requested here
      { emplace_back(std::move(__x)); }
        ^
experiment/miniso.cpp:24:5: note: in instantiation of member function 'std::vector<Foo, std::allocator<Foo> >::push_back' requested here
  v.push_back(Foo());
    ^
experiment/miniso.cpp:19:3: note: 'Foo' has been explicitly marked deleted here
  Foo(const Foo &) = delete;
  ^

我知道一些关于什么可以 move 什么不能 move 的规则,但这似乎并不容易处理。为什么会发生这种情况?如何解决这个问题,使其不调用复制构造函数?

目标编译器是 Clang C++14。

最佳答案

它正在尝试调用您的复制构造函数,因为您的 move 构造函数已删除。哦当然,我知道你写了 = default。但是由于 union 包含一个具有非平凡 move/复制构造函数的类型,如果用户未提供 union 的复制/move 构造函数,它将被隐式删除。

并且 = default 不会使其成为“用户提供的”。

换句话说,如果 union 的任何成员需要复制/move 代码而不是 memcpy (又名:不可简单复制)。因此,包含 union 的类型也不能有编译器生成的复制/move 代码。在这些情况下,您必须决定如何复制/move 对象。

这样的复制/move 构造函数需要知道它是哪种类型。

关于c++ - 当 "moved"对象在 union 中有一个 "non-trivial"成员时,为什么要强制复制构造函数?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47106117/

相关文章:

javascript - JQuery - 获取 <p>太多溢出!</p> 的 .x .y 坐标,然后 move <div>

c++ - 转到其他逻辑驱动器并继续搜索文件

C++ 的性能比 C 更好?

c++ - 通用 lambda 在 C++14 中如何工作?

cuda - 使用 cuda 在设备上进行动态堆栈分配

c++ - 为什么当 T 是类类型时 std::is_base_of<T, T> 为真,而当 T 是内置类型时为假?

c++ - 从类型 'delai_assemblage*&' 的表达式对类型 'delai_assemblage' 的引用的初始化无效

c++ - 红黑树重新平衡在树旋转时崩溃

javascript - HTML 5/JS 中获取 Canvas 对象位置并 move 到 Canvas 触摸位置

file - 如何在 Windows 8.1 中 move 路径名 > 255 个字符的文件夹/文件?