当 "moved" 对象在联合中有一个 "non-trivial" 成员时,为什么要强制复制构造函数?
Why copy constructor is forced when the "moved" object has a "non-trivial" member in a union?
我注意到 move
可以应用于 而不是 与 "non-trivial" 并集的对象(不完全知道,但是例如原始类型很好)成员。例如,以下代码编译(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::vector
的 push_back
可以接受右值引用,因此在这种情况下它将使用它,并且不会出现 copy
。但是,一旦将 "non-trivial" 类型添加到联合中,就会强制复制构造函数 - 因此它不会编译:
#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;
^
我知道一些关于什么可以移动什么不能移动的规则,但这似乎并不容易处理。为什么会发生这种情况,如何解决它不调用复制构造函数?
目标编译器是 Clang C++14。
它正在尝试调用您的复制构造函数,因为您的移动构造函数已删除。哦当然,我知道你写了 = default
。但是由于联合包含一个具有非平凡 move/copy 构造函数的类型,如果用户未提供联合的 copy/move 构造函数,它将被隐式删除。
而 = default
没有做到 "user provided"。
换句话说,编译器不能给 union
一个 copy/move 构造函数,如果它的任何成员需要 copying/moving 代码而不是 memcpy
(又名:不可简单复制)。因此,包含联合的类型也不能有编译器生成的 copying/moving 代码。在这些情况下,必须由您来决定如何 copy/move 对象。
而这样的copy/move构造函数需要知道它是什么类型。
我注意到 move
可以应用于 而不是 与 "non-trivial" 并集的对象(不完全知道,但是例如原始类型很好)成员。例如,以下代码编译(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::vector
的 push_back
可以接受右值引用,因此在这种情况下它将使用它,并且不会出现 copy
。但是,一旦将 "non-trivial" 类型添加到联合中,就会强制复制构造函数 - 因此它不会编译:
#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;
^
我知道一些关于什么可以移动什么不能移动的规则,但这似乎并不容易处理。为什么会发生这种情况,如何解决它不调用复制构造函数?
目标编译器是 Clang C++14。
它正在尝试调用您的复制构造函数,因为您的移动构造函数已删除。哦当然,我知道你写了 = default
。但是由于联合包含一个具有非平凡 move/copy 构造函数的类型,如果用户未提供联合的 copy/move 构造函数,它将被隐式删除。
而 = default
没有做到 "user provided"。
换句话说,编译器不能给 union
一个 copy/move 构造函数,如果它的任何成员需要 copying/moving 代码而不是 memcpy
(又名:不可简单复制)。因此,包含联合的类型也不能有编译器生成的 copying/moving 代码。在这些情况下,必须由您来决定如何 copy/move 对象。
而这样的copy/move构造函数需要知道它是什么类型。