如何修复具有unique_ptr的对象的向量

时间:2018-04-18 01:26:29

标签: c++ c++11 vector stdvector unique-ptr

我有以下代码,其中包含一个类的向量,该类具有一些声明为unique_ptr的成员。

struct Container
{
    struct Nested{
        std::unique_ptr<Container> node;

        Nested(std::unique_ptr<Container> t) : node(std::move(t)) {}
        Nested(const Nested& t) { node = std::move(t.node); };
    };

    std::vector<Nested> edges;
};

typedef std::unique_ptr<Container> UCont;
typedef Container::Nested Nested;


int main()
{
    std::unique_ptr<Container> object = UCont(new Container{{
                                    Nested(UCont(new Container{{}})),
                                    Nested(UCont(new Container{{}})),
                                    Nested(UCont(new Container{{}}))
                                }});
}

现在编译它会产生以下错误:

..\00.UniquePtrVector.cpp: In copy constructor 'Container::Nested::Nested(const Container::Nested&)':
..\00.UniquePtrVector.cpp:20:35: error: use of deleted function 'std::unique_ptr<_Tp, _Dp>& std::unique_ptr<_Tp, _Dp>::operator=(const std::unique_ptr<_Tp, _Dp>&) [with _Tp = Container; _Dp = std::default_delete<Container>]'
    Nested(const Nested& t) { node = std::move(t.node); };
                                   ^
In file included from c:\mingw\lib\gcc\mingw32\5.3.0\include\c++\bits\locale_conv.h:41:0,
                 from c:\mingw\lib\gcc\mingw32\5.3.0\include\c++\locale:43,
                 from c:\mingw\lib\gcc\mingw32\5.3.0\include\c++\iomanip:43,
                 from c:\mingw\lib\gcc\mingw32\5.3.0\include\c++\mingw32\bits\stdc++.h:71,
                 from ..\00.UniquePtrVector.cpp:10:
c:\mingw\lib\gcc\mingw32\5.3.0\include\c++\bits\unique_ptr.h:357:19: note: declared here
       unique_ptr& operator=(const unique_ptr&) = delete;

我不确定如何解决此错误。我想删除拷贝构造函数也不是一个选项。有什么帮助吗?

编辑: 改为

Nested(std::unique_ptr<Container>&& t) : node(std::move(t)) {}
Nested(Nested&& t) : node(std::move(t.node)) {}
Nested(const Nested& t) =delete;

也给出错误:

In file included from c:\mingw\lib\gcc\mingw32\5.3.0\include\c++\bits\stl_tempbuf.h:60:0,
                 from c:\mingw\lib\gcc\mingw32\5.3.0\include\c++\bits\stl_algo.h:62,
                 from c:\mingw\lib\gcc\mingw32\5.3.0\include\c++\algorithm:62,
                 from c:\mingw\lib\gcc\mingw32\5.3.0\include\c++\mingw32\bits\stdc++.h:64,
                 from ..\00.UniquePtrVector.cpp:10:
c:\mingw\lib\gcc\mingw32\5.3.0\include\c++\bits\stl_construct.h: In instantiation of 'void std::_Construct(_T1*, _Args&& ...) [with _T1 = Container::Nested; _Args = {const Container::Nested&}]':
c:\mingw\lib\gcc\mingw32\5.3.0\include\c++\bits\stl_uninitialized.h:75:18:   required from 'static _ForwardIterator std::__uninitialized_copy<_TrivialValueTypes>::__uninit_copy(_InputIterator, _InputIterator, _ForwardIterator) [with _InputIterator = const Container::Nested*; _ForwardIterator = Container::Nested*; bool _TrivialValueTypes = false]'
c:\mingw\lib\gcc\mingw32\5.3.0\include\c++\bits\stl_uninitialized.h:126:15:   required from '_ForwardIterator std::uninitialized_copy(_InputIterator, _InputIterator, _ForwardIterator) [with _InputIterator = const Container::Nested*; _ForwardIterator = Container::Nested*]'
c:\mingw\lib\gcc\mingw32\5.3.0\include\c++\bits\stl_uninitialized.h:281:37:   required from '_ForwardIterator std::__uninitialized_copy_a(_InputIterator, _InputIterator, _ForwardIterator, std::allocator<_Tp>&) [with _InputIterator = const Container::Nested*; _ForwardIterator = Container::Nested*; _Tp = Container::Nested]'
c:\mingw\lib\gcc\mingw32\5.3.0\include\c++\bits\stl_vector.h:1290:33:   required from 'void std::vector<_Tp, _Alloc>::_M_range_initialize(_ForwardIterator, _ForwardIterator, std::forward_iterator_tag) [with _ForwardIterator = const Container::Nested*; _Tp = Container::Nested; _Alloc = std::allocator<Container::Nested>]'
c:\mingw\lib\gcc\mingw32\5.3.0\include\c++\bits\stl_vector.h:377:21:   required from 'std::vector<_Tp, _Alloc>::vector(std::initializer_list<_Tp>, const allocator_type&) [with _Tp = Container::Nested; _Alloc = std::allocator<Container::Nested>; std::vector<_Tp, _Alloc>::allocator_type = std::allocator<Container::Nested>]'
..\00.UniquePtrVector.cpp:36:11:   required from here
c:\mingw\lib\gcc\mingw32\5.3.0\include\c++\bits\stl_construct.h:75:7: error: use of deleted function 'Container::Nested::Nested(const Container::Nested&)'
     { ::new(static_cast<void*>(__p)) _T1(std::forward<_Args>(__args)...); }
       ^
..\00.UniquePtrVector.cpp:20:11: note: declared here
           Nested(const Nested& t) =delete;
           ^

3 个答案:

答案 0 :(得分:2)

当您在复制构造函数中移动t.node时,t.node需要更改。但t是常量,因此移动无效。 unique_ptr无法复制构造,因此struct Nested也不能复制构造。

要使其工作,您需要提供移动构造函数并删除复制构造函数。像这样:

    struct Nested{
        std::unique_ptr<Container> node;
        Nested(std::unique_ptr<Container>&& t) : node(std::move(t)) {}
        Nested(Nested&& t) : node(std::move(t.node)) {}
        Nested(const Nested& t) =delete;
    };

答案 1 :(得分:2)

tl; dr:考虑使用rule of zero

我会谨慎地明确指定你班级中的所有构造函数。当你按照你想要的方式做事时,unique_ptr无法复制的事实会让你遇到各种各样的麻烦。当您没有指定任何构造函数时,会发生以下情况:

#include <vector>
#include <memory>

struct Container
{
    struct Nested{
        std::unique_ptr<Container> node;
//
//        Nested(std::unique_ptr<Container> t) : node(std::move(t)) {}
//        Nested(const Nested& t) { node = std::move(t.node); };
    };

    std::vector<Nested> edges;
};

typedef std::unique_ptr<Container> UCont;
typedef Container::Nested Nested;


int main()
{
    auto c1 = new Container{};
    auto c2 = new Container{};
    auto c3 = new Container{};
    std::unique_ptr<Container> u1 {c1};
    std::unique_ptr<Container> u2 {c2};
    std::unique_ptr<Container> u3 {c3};
    Nested n1 {std::move(u1)};
    Nested n2 {std::move(u2)};
    Nested n3 {std::move(u3)};
    auto v = std::vector<Nested>{3};
    v.push_back(std::move(n1));
    v.push_back(std::move(n2));
    v.push_back(std::move(n3));
    auto c5 = new Container { std::move(v) };
    std::unique_ptr<Container> object = UCont(std::move(c5));
}

为了清晰起见,我把所有内容都归结为较短的陈述(主要是)。

答案 2 :(得分:0)

我得到的错误的根本原因是由于在初始化程序列表中使用的对象内部使用了unique_ptr。与简单unique_ptr的向量(如此处Initializing container of unique_ptrs from initializer list fails with GCC 4.7)类似,在其数据模型中也具有unique_ptr的对象也不能在初始化列表中使用。

与其他链接类似,原因相同;初始化列表始终执行副本,unique_ptr无法复制。因此,我们必须使用emplace_back / push_back

因此,即使使用构造函数,以下解决方案仍可正常工作

struct Container
{
    struct Nested{
            std::unique_ptr<Container> node;

            Nested(): node(nullptr) {}
            Nested(std::unique_ptr<Container> t) : node(std::move(t)) {}
        };

    std::vector<Nested> edges;
};

typedef std::unique_ptr<Container> UCont;
typedef Container::Nested Nested;


int main()
{
        auto v = std::vector<Nested>{3};
        v.push_back(std::move(Nested(std::move(std::unique_ptr<Container>(new Container{})))));
        v.push_back(std::move(Nested(std::move(std::unique_ptr<Container>(new Container{})))));
        v.push_back(std::move(Nested(std::move(std::unique_ptr<Container>(new Container{})))));
        std::unique_ptr<Container> object = UCont(new Container { std::move(v) });

}