如何移动包含 unique_ptr 作为成员的对象矢量?
How do I move a vector of objects that contain a unique_ptr as a member?
我有一个函数,returns class 的 std::vector
包含 std::unique_ptr
作为成员。我需要将这个矢量对象存储在堆上,以便我可以通过 C 风格的 DLL 接口传递它。
参见以下代码示例:
#include <iostream>
#include <cstdlib>
#include <memory>
#include <vector>
// I have control over the following two classes
class SomeBigClassWithManyMembers { };
class MyClass
{
std::unique_ptr<SomeBigClassWithManyMembers> up;
public:
static const std::vector<MyClass> GetMany()
{
// imagine this does lots of work
return std::vector<MyClass>(50);
}
// following code is suggested in but doesn't help
/*
MyClass() { }
MyClass(MyClass&& other): up{std::move(other.up)} { }
MyClass& operator=(MyClass&& other)
{
up = std::move(other.up);
return *this;
}
*/
};
// Imagine that I can't change this C-style code - it's a fixed interface provided by someone else
struct NastyCStyleStruct
{
void* v;
};
void NastyCStyleInterface(NastyCStyleStruct s) { printf("%u", (unsigned int)((std::vector<MyClass>*)s.v)->size()); }
int main()
{
NastyCStyleStruct s;
s.v = new std::vector<MyClass>(std::move(MyClass::GetMany()));
NastyCStyleInterface(s);
return 0;
}
请注意,在我的实际代码中,向量需要比创建它的函数长寿(因为这是在 DLL 中完成的),因此写作
auto vec = MyClass::GetMany();
s.v = &vec;
不够。向量必须存储在堆上。
这里的问题是代码似乎试图使用 MyClass
的(不存在的)复制构造函数。我不明白为什么要调用复制构造函数,因为我明确要求 std::move
的移动语义。甚至没有将 s.v
初始化为适当大小的新 new std::vector
并调用 std::move
的三参数版本。
来自 g++ 的错误:
In file included from /usr/include/c++/7/memory:64:0,
from stackq.cpp:4:
/usr/include/c++/7/bits/stl_construct.h: In instantiation of ‘void std::_Construct(_T1*, _Args&& ...) [with _T1 = MyClass; _Args = {const MyClass&}]’:
/usr/include/c++/7/bits/stl_uninitialized.h:83:18: required from ‘static _ForwardIterator std::__uninitialized_copy<_TrivialValueTypes>::__uninit_copy(_InputIterator, _InputIterator, _ForwardIterator) [with _InputIterator = __gnu_cxx::__normal_iterator<const MyClass*, std::vector<MyClass> >; _ForwardIterator = MyClass*; bool _TrivialValueTypes = false]’
/usr/include/c++/7/bits/stl_uninitialized.h:134:15: required from ‘_ForwardIterator std::uninitialized_copy(_InputIterator, _InputIterator, _ForwardIterator) [with _InputIterator = __gnu_cxx::__normal_iterator<const MyClass*, std::vector<MyClass> >; _ForwardIterator = MyClass*]’
/usr/include/c++/7/bits/stl_uninitialized.h:289:37: required from ‘_ForwardIterator std::__uninitialized_copy_a(_InputIterator, _InputIterator, _ForwardIterator, std::allocator<_Tp>&) [with _InputIterator = __gnu_cxx::__normal_iterator<const MyClass*, std::vector<MyClass> >; _ForwardIterator = MyClass*; _Tp = MyClass]’
/usr/include/c++/7/bits/stl_vector.h:331:31: required from ‘std::vector<_Tp, _Alloc>::vector(const std::vector<_Tp, _Alloc>&) [with _Tp = MyClass; _Alloc = std::allocator<MyClass>]’
stackq.cpp:43:65: required from here
/usr/include/c++/7/bits/stl_construct.h:75:7: error: use of deleted function ‘MyClass::MyClass(const MyClass&)’
{ ::new(static_cast<void*>(__p)) _T1(std::forward<_Args>(__args)...); }
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
stackq.cpp:10:7: note: ‘MyClass::MyClass(const MyClass&)’ is implicitly deleted because the default definition would be ill-formed:
class MyClass
^~~~~~~
stackq.cpp:10:7: error: use of deleted function ‘std::unique_ptr<_Tp, _Dp>::unique_ptr(const std::unique_ptr<_Tp, _Dp>&) [with _Tp = SomeBigClassWithManyMembers; _Dp = std::default_delete<SomeBigClassWithManyMembers>]’
In file included from /usr/include/c++/7/memory:80:0,
from stackq.cpp:4:
/usr/include/c++/7/bits/unique_ptr.h:388:7: note: declared here
unique_ptr(const unique_ptr&) = delete;
^~~~~~~~~~
如何修复此代码,以便向量对象本身存储在堆上?
这里的问题是GetMany
被定义为return一个const std::vector
。即使它是按值 returning,所以如果你改变结果也不会有不必要的副作用的风险,编译器仍然强制执行类型(auto
只是复制了确切的 return 类型毕竟是功能),因此无法从中移动。因此,与其廉价地复制一些指针/size_t
大小的值(到 vector
内容、大小和容量),它必须进行完整的复制构造,包括复制构造存储在原文vector
。由于存储实例中的 unique_ptr
成员,该操作失败。
简单地从 return 类型中删除 const
应该允许 std::move
/vector
的移动构造函数完成它们的工作,允许超便宜的提取由 GetMany
编辑的 vector
return 的内容没有 MyClass
.
的任何实例的副本(甚至移动)
我有一个函数,returns class 的 std::vector
包含 std::unique_ptr
作为成员。我需要将这个矢量对象存储在堆上,以便我可以通过 C 风格的 DLL 接口传递它。
参见以下代码示例:
#include <iostream>
#include <cstdlib>
#include <memory>
#include <vector>
// I have control over the following two classes
class SomeBigClassWithManyMembers { };
class MyClass
{
std::unique_ptr<SomeBigClassWithManyMembers> up;
public:
static const std::vector<MyClass> GetMany()
{
// imagine this does lots of work
return std::vector<MyClass>(50);
}
// following code is suggested in but doesn't help
/*
MyClass() { }
MyClass(MyClass&& other): up{std::move(other.up)} { }
MyClass& operator=(MyClass&& other)
{
up = std::move(other.up);
return *this;
}
*/
};
// Imagine that I can't change this C-style code - it's a fixed interface provided by someone else
struct NastyCStyleStruct
{
void* v;
};
void NastyCStyleInterface(NastyCStyleStruct s) { printf("%u", (unsigned int)((std::vector<MyClass>*)s.v)->size()); }
int main()
{
NastyCStyleStruct s;
s.v = new std::vector<MyClass>(std::move(MyClass::GetMany()));
NastyCStyleInterface(s);
return 0;
}
请注意,在我的实际代码中,向量需要比创建它的函数长寿(因为这是在 DLL 中完成的),因此写作
auto vec = MyClass::GetMany();
s.v = &vec;
不够。向量必须存储在堆上。
这里的问题是代码似乎试图使用 MyClass
的(不存在的)复制构造函数。我不明白为什么要调用复制构造函数,因为我明确要求 std::move
的移动语义。甚至没有将 s.v
初始化为适当大小的新 new std::vector
并调用 std::move
的三参数版本。
来自 g++ 的错误:
In file included from /usr/include/c++/7/memory:64:0,
from stackq.cpp:4:
/usr/include/c++/7/bits/stl_construct.h: In instantiation of ‘void std::_Construct(_T1*, _Args&& ...) [with _T1 = MyClass; _Args = {const MyClass&}]’:
/usr/include/c++/7/bits/stl_uninitialized.h:83:18: required from ‘static _ForwardIterator std::__uninitialized_copy<_TrivialValueTypes>::__uninit_copy(_InputIterator, _InputIterator, _ForwardIterator) [with _InputIterator = __gnu_cxx::__normal_iterator<const MyClass*, std::vector<MyClass> >; _ForwardIterator = MyClass*; bool _TrivialValueTypes = false]’
/usr/include/c++/7/bits/stl_uninitialized.h:134:15: required from ‘_ForwardIterator std::uninitialized_copy(_InputIterator, _InputIterator, _ForwardIterator) [with _InputIterator = __gnu_cxx::__normal_iterator<const MyClass*, std::vector<MyClass> >; _ForwardIterator = MyClass*]’
/usr/include/c++/7/bits/stl_uninitialized.h:289:37: required from ‘_ForwardIterator std::__uninitialized_copy_a(_InputIterator, _InputIterator, _ForwardIterator, std::allocator<_Tp>&) [with _InputIterator = __gnu_cxx::__normal_iterator<const MyClass*, std::vector<MyClass> >; _ForwardIterator = MyClass*; _Tp = MyClass]’
/usr/include/c++/7/bits/stl_vector.h:331:31: required from ‘std::vector<_Tp, _Alloc>::vector(const std::vector<_Tp, _Alloc>&) [with _Tp = MyClass; _Alloc = std::allocator<MyClass>]’
stackq.cpp:43:65: required from here
/usr/include/c++/7/bits/stl_construct.h:75:7: error: use of deleted function ‘MyClass::MyClass(const MyClass&)’
{ ::new(static_cast<void*>(__p)) _T1(std::forward<_Args>(__args)...); }
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
stackq.cpp:10:7: note: ‘MyClass::MyClass(const MyClass&)’ is implicitly deleted because the default definition would be ill-formed:
class MyClass
^~~~~~~
stackq.cpp:10:7: error: use of deleted function ‘std::unique_ptr<_Tp, _Dp>::unique_ptr(const std::unique_ptr<_Tp, _Dp>&) [with _Tp = SomeBigClassWithManyMembers; _Dp = std::default_delete<SomeBigClassWithManyMembers>]’
In file included from /usr/include/c++/7/memory:80:0,
from stackq.cpp:4:
/usr/include/c++/7/bits/unique_ptr.h:388:7: note: declared here
unique_ptr(const unique_ptr&) = delete;
^~~~~~~~~~
如何修复此代码,以便向量对象本身存储在堆上?
这里的问题是GetMany
被定义为return一个const std::vector
。即使它是按值 returning,所以如果你改变结果也不会有不必要的副作用的风险,编译器仍然强制执行类型(auto
只是复制了确切的 return 类型毕竟是功能),因此无法从中移动。因此,与其廉价地复制一些指针/size_t
大小的值(到 vector
内容、大小和容量),它必须进行完整的复制构造,包括复制构造存储在原文vector
。由于存储实例中的 unique_ptr
成员,该操作失败。
简单地从 return 类型中删除 const
应该允许 std::move
/vector
的移动构造函数完成它们的工作,允许超便宜的提取由 GetMany
编辑的 vector
return 的内容没有 MyClass
.