C++ 如何移动包含唯一_ptr作为成员的对象向量?

C++ 如何移动包含唯一_ptr作为成员的对象向量?,c++,move,stdvector,unique-ptr,C++,Move,Stdvector,Unique Ptr,我有一个函数,它返回一个类的std::vector,该类包含一个std::unique\u ptr作为成员。我需要将这个向量对象存储在堆上,这样我就可以通过C风格的DLL接口传递它 请参阅以下代码示例: #include <iostream> #include <cstdlib> #include <memory> #include <vector> // I have control over the following two classe

我有一个函数,它返回一个类的
std::vector
,该类包含一个
std::unique\u 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 https://stackoverflow.com/questions/31430533/move-assignable-class-containing-vectorunique-ptrt 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;
}
不够。向量必须存储在堆上

这里的问题是代码似乎试图使用
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;
       ^~~~~~~~~~
在/usr/include/c++/7/memory:64:0中包含的文件中,
来自stackq.cpp:4:
/usr/include/c++/7/bits/stl_-construct.h:在“void std::_-construct(_-T1*,_-Args&&&…[其中_-T1=MyClass;_-Args={const-MyClass&}]”的实例化中:
/usr/include/c++/7/bits/stl_uninitialized.h:83:18:从“静态”转发器std::__uninitialized_copy::_uninitialized_copy(_inputierator,_inputierator,_ForwardIterator,_ForwardIterator)[带_inputierator=u gnu cxx:_normal u迭代器;_ForwardIterator=MyClass*;bool _-valuetypes=false]”
/usr/include/c++/7/bits/stl\u uninitialized.h:134:15:从“\u ForwardIterator std::uninitialized\u copy(\u InputIterator,\u InputIterator,\u ForwardIterator,\u ForwardIterator)[with\u InputIterator=\u gnu\u cxx::\u normal\u ForwardIterator=MyClass*]中需要
/usr/include/c++/7/bits/stl_uninitialized.h:289:37:从“_forwarditeratorstd::__uninitialized_copy_a(_inputierator,_inputierator,_ForwardIterator,std::allocator&”)中需要[with_inputierator=_gnu_cxx:_normal_iterator;_ForwardIterator=MyClass*_Tp=MyClass]”
/usr/include/c++/7/bits/stl_vector.h:331:31:必须来自“std::vector::vector(const std::vector&)[with _Tp=MyClass;_Alloc=std::allocator]”
stackq.cpp:43:65:从这里开始需要
/usr/include/c++/7/bits/stl_construct.h:75:7:错误:使用了已删除的函数“MyClass::MyClass(const MyClass&)”
{::新建(静态_-cast(u-p))_-T1(标准::转发(u-args));}
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
stackq.cpp:10:7:注意:“MyClass::MyClass(const MyClass&)”被隐式删除,因为默认定义的格式可能不正确:
类MyClass
^~~~~~~
stackq.cpp:10:7:错误:使用删除的函数“std::unique\u ptr::unique\u ptr(const std::unique\u ptr&)[使用_Tp=SomeBigClassWithManyMembers;_Dp=std::default\u delete]”
在/usr/include/c++/7/memory:80:0中包含的文件中,
来自stackq.cpp:4:
/usr/include/c++/7/bits/unique_ptr.h:388:7:注意:此处声明
唯一\u ptr(const unique \u ptr&)=删除;
^~~~~~~~~~

如何修复此代码以使向量对象本身存储在堆上?

这里的问题是
GetMany
被定义为返回
const std::vector
。即使它是按值返回的,因此如果您改变结果,也不会产生不必要的副作用,编译器仍然强制执行该类型(
auto
毕竟只是复制了函数的确切返回类型),因此无法从中移动。因此,与其廉价地复制几个指针大小的值(到
向量
内容、大小和容量),它必须进行完整的复制构造,包括复制构造原始
向量
中存储的所有值。由于存储实例中的
唯一\u ptr
成员,该操作失败


只需从返回类型中删除
const
就可以让
std::move
/
vector
的move构造函数完成它们的工作,从而可以超便宜地提取
GetMany
返回的
vector
的内容,而无需复制(甚至移动)对于
MyClass

的任何实例,都会调用copy构造函数,因为
GetMany
被定义为返回
const std::vector
,而不是
std::vector
,因此
std::move
实际上无法将其转换为r值;离开它将违反
常量
-ness,因此复制是唯一的选择。很可能你不需要堆上的
向量
(虽然它可能会使代码更容易),你只需要停止禁止复制省略/移动语义。啊,是的,我真傻,没有注意到这一点!删除
GetMany
上的
const
现在可以编译代码了。@MarekR:OP的代码似乎是为了使用一个奇怪的C类接口,所以它们可能没有太多选择。@ShadowRanger我知道这一点,但将指向
std::vector
的指针传递给C API显然是错误的(我不认为这种情况可能是正确的)。很可能他需要
std::vector::data
返回的值。还要注意的是,他是个新手,所以这一定是置换编程的结果。事实上,我怀疑。
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;
       ^~~~~~~~~~