提升(反)序列化派生对象的向量,使用已删除的函数(unique_ptr)

Posted

技术标签:

【中文标题】提升(反)序列化派生对象的向量,使用已删除的函数(unique_ptr)【英文标题】:Boost (de)serialize vector of dervied objects, use of deleted finction (unique_ptr) 【发布时间】:2018-06-21 11:51:03 【问题描述】:

我正在尝试在我的项目中实现序列化。我的问题是,当我尝试反序列化对象时,出现如下错误:

/usr/include/c++/5/ext/new_allocator.h:120:4: error: use of deleted function ‘std::unique_ptr<_Tp, _Dp>::unique_ptr(const std::unique_ptr<_Tp, _Dp>&) [with _Tp = Employee; _Dp = std::default_delete<Employee>]’
   ::new((void *)__p) _Up(std::forward<_Args>(__args)...); 

编辑:我添加了hireInternemployeeMenu 函数并减少了一些不必要的代码。

class Employee   
protected:    
    int employeeID;
    std::string Name;
    std::string Surname;
    int Salary;
    bool Hired;   
public:   
    friend class boost::serialization::access;

    template<typename Archive>
    void serialize(Archive & ar, const unsigned int file_version) 
        ar & employeeID;
        ar & Name;
        ar & Surname;
        ar & Salary;
        ar & Hired;

        
    virtual ~Employee();
    Employee();
    Employee(const std::string &, const std::string &);

.cpp

#include "Employee.h"   
Employee::~Employee() = default;    
Employee::Employee() = default;   
Employee::Employee(const std::string &newName, const std::string &newSurname) : Name(newName), Surname(newSurname) 

派生,“实习生.h”

class Intern : public Employee 
public:
    friend class boost::serialization::access;

    template<typename Archive>
    void serialize(Archive & ar, const unsigned int file_version) 
        ar & boost::serialization::base_object<Employee>(*this);
        ar & Status;
    

    std::string Status = "Intern";

    Intern();
    ~Intern();
    Intern(const std::string &, const std::string &);

;
BOOST_CLASS_EXPORT_KEY(Intern);

.cpp

#include "Intern.h"   
BOOST_CLASS_EXPORT_IMPLEMENT(Intern);    
Intern::Intern() = default;   
Intern::~Intern() = default;    
Intern::Intern(const std::string &newName, const std::string &newSurname) : Employee(newName, newSurname) 

开头的注释行导致反序列化错误。 当我忽略它们时,case 3: 中实现的序列化工作正常。 问题是,我还想在开始时将对象从 .txt 文件加载到向量中。

void mainMenu() 

    //std::ifstream ifs("database");
    //boost::archive::text_iarchive ia(ifs);

    vector<unique_ptr<Employee>> Firm;

    //ia >> boost::serialization::make_nvp("root", Firm);
...

        switch (option) 
            case 1:
                hireIntern(Firm);
                break;
            case 2:
                employeeMenu(Firm);
                break;
            case 3:
            
                std::ofstream ofs("database");
                boost::archive::text_oarchive oa(ofs);
                oa << boost::serialization::make_nvp("root", Firm);

                return;
            
...

.

void hireIntern(vector<unique_ptr<Employee>>& sourceEmployee) 
    ...
        sourceEmployee.emplace_back(new Intern(fillName, fillSurname));

。在employeeMenu 中调用的函数中,我只是在编辑一些Firm 对象字段向量,例如sourceEmployee[index]-&gt;setSalary(sourceEmployee[index]-&gt;getSalary() + 1000);

  void employeeMenu(vector<unique_ptr<Employee>>& sourceEmployee) 

...

        switch (option) 

            case 1:
                Promote(sourceEmployee);
                break;
            case 2:
                Demote(sourceEmployee);
                break;
            case 3:
                fireEmployee(sourceEmployee);
                break;
            case 4:
                employeeShowcase(sourceEmployee);
                break;
            case 5:
                showHiredEmployees(sourceEmployee);
                break;
            case 6:
                showFiredEmployees(sourceEmployee);

当我使用这些行编译代码时,出现以下错误:

/usr/local/bin/cmake --build /home/max/c++/PROJEKT/cmake-build-debug --target PROJEKT -- -j 2
Scanning dependencies of target PROJEKT
[ 12%] Building CXX object CMakeFiles/PROJEKT.dir/mainMenu.cpp.o
In file included from /usr/include/x86_64-linux-gnu/c++/5/bits/c++allocator.h:33:0,
                 from /usr/include/c++/5/bits/allocator.h:46,
                 from /usr/include/c++/5/string:41,
                 from /usr/include/c++/5/bits/locale_classes.h:40,
                 from /usr/include/c++/5/bits/ios_base.h:41,
                 from /usr/include/c++/5/ios:42,
                 from /usr/include/c++/5/istream:38,
                 from /usr/include/c++/5/fstream:38,
                 from /home/max/c++/PROJEKT/Employee.h:8,
                 from /home/max/c++/PROJEKT/manageDatabase.h:8,
                 from /home/max/c++/PROJEKT/mainMenu.cpp:5:
/usr/include/c++/5/ext/new_allocator.h: In instantiation of ‘void __gnu_cxx::new_allocator<_Tp>::construct(_Up*, _Args&& ...) [with _Up = std::unique_ptr<Employee>; _Args = const std::unique_ptr<Employee, std::default_delete<Employee> >&; _Tp = std::unique_ptr<Employee>]’:
/usr/include/c++/5/bits/alloc_traits.h:530:4:   required from ‘static void std::allocator_traits<std::allocator<_CharT> >::construct(std::allocator_traits<std::allocator<_CharT> >::allocator_type&, _Up*, _Args&& ...) [with _Up = std::unique_ptr<Employee>; _Args = const std::unique_ptr<Employee, std::default_delete<Employee> >&; _Tp = std::unique_ptr<Employee>; std::allocator_traits<std::allocator<_CharT> >::allocator_type = std::allocator<std::unique_ptr<Employee> >]’
/usr/include/c++/5/bits/stl_vector.h:917:30:   required from ‘void std::vector<_Tp, _Alloc>::push_back(const value_type&) [with _Tp = std::unique_ptr<Employee>; _Alloc = std::allocator<std::unique_ptr<Employee> >; std::vector<_Tp, _Alloc>::value_type = std::unique_ptr<Employee>]’
/usr/local/include/boost/serialization/vector.hpp:102:13:   required from ‘void boost::serialization::load(Archive&, std::vector<U, Allocator>&, unsigned int, mpl_::false_) [with Archive = boost::archive::text_iarchive; U = std::unique_ptr<Employee>; Allocator = std::allocator<std::unique_ptr<Employee> >; mpl_::false_ = mpl_::bool_<false>]’
/usr/local/include/boost/serialization/vector.hpp:173:9:   required from ‘void boost::serialization::load(Archive&, std::vector<U, Allocator>&, unsigned int) [with Archive = boost::archive::text_iarchive; U = std::unique_ptr<Employee>; Allocator = std::allocator<std::unique_ptr<Employee> >]’
/usr/local/include/boost/serialization/split_free.hpp:58:13:   required from ‘static void boost::serialization::free_loader<Archive, T>::invoke(Archive&, T&, unsigned int) [with Archive = boost::archive::text_iarchive; T = std::vector<std::unique_ptr<Employee> >]’
/usr/local/include/boost/serialization/split_free.hpp:74:18:   [ skipping 22 instantiation contexts, use -ftemplate-backtrace-limit=0 to disable ]
/usr/local/include/boost/archive/detail/iserializer.hpp:618:18:   required from ‘void boost::archive::load(Archive&, T&) [with Archive = boost::archive::text_iarchive; T = const boost::serialization::nvp<std::vector<std::unique_ptr<Employee> > >]’
/usr/local/include/boost/archive/detail/common_iarchive.hpp:66:22:   required from ‘void boost::archive::detail::common_iarchive<Archive>::load_override(T&, int) [with T = const boost::serialization::nvp<std::vector<std::unique_ptr<Employee> > >; Archive = boost::archive::text_iarchive]’
/usr/local/include/boost/archive/basic_text_iarchive.hpp:71:9:   required from ‘void boost::archive::basic_text_iarchive<Archive>::load_override(T&, int) [with T = const boost::serialization::nvp<std::vector<std::unique_ptr<Employee> > >; Archive = boost::archive::text_iarchive]’
/usr/local/include/boost/archive/text_iarchive.hpp:92:52:   required from ‘void boost::archive::text_iarchive_impl<Archive>::load_override(T&, int) [with T = const boost::serialization::nvp<std::vector<std::unique_ptr<Employee> > >; Archive = boost::archive::text_iarchive]’
/usr/local/include/boost/archive/detail/interface_iarchive.hpp:60:9:   required from ‘Archive& boost::archive::detail::interface_iarchive<Archive>::operator>>(T&) [with T = const boost::serialization::nvp<std::vector<std::unique_ptr<Employee> > >; Archive = boost::archive::text_iarchive]’
/home/max/c++/PROJEKT/mainMenu.cpp:16:54:   required from here
/usr/include/c++/5/ext/new_allocator.h:120:4: error: use of deleted function ‘std::unique_ptr<_Tp, _Dp>::unique_ptr(const std::unique_ptr<_Tp, _Dp>&) [with _Tp = Employee; _Dp = std::default_delete<Employee>]’
   ::new((void *)__p) _Up(std::forward<_Args>(__args)...); 
    ^
In file included from /usr/include/c++/5/bits/locale_conv.h:41:0,
                 from /usr/include/c++/5/locale:43,
                 from /usr/include/c++/5/iomanip:43,
                 from /usr/local/include/boost/archive/basic_text_oprimitive.hpp:27,
                 from /usr/local/include/boost/archive/text_oarchive.hpp:30,
                 from /home/max/c++/PROJEKT/Employee.h:9,
                 from /home/max/c++/PROJEKT/manageDatabase.h:8,
                 from /home/max/c++/PROJEKT/mainMenu.cpp:5:
/usr/include/c++/5/bits/unique_ptr.h:356:7: note: declared here
       unique_ptr(const unique_ptr&) = delete;
       ^
CMakeFiles/PROJEKT.dir/build.make:114: recipe for target 'CMakeFiles/PROJEKT.dir/mainMenu.cpp.o' failed
make[3]: *** [CMakeFiles/PROJEKT.dir/mainMenu.cpp.o] Error 1
CMakeFiles/Makefile2:67: recipe for target 'CMakeFiles/PROJEKT.dir/all' failed
make[2]: *** [CMakeFiles/PROJEKT.dir/all] Error 2
CMakeFiles/Makefile2:79: recipe for target 'CMakeFiles/PROJEKT.dir/rule' failed
make[1]: *** [CMakeFiles/PROJEKT.dir/rule] Error 2
Makefile:118: recipe for target 'PROJEKT' failed
make: *** [PROJEKT] Error 2

【问题讨论】:

请提供Minimal, Complete, and Verifiable example 您试图复制unique_ptr 的位置。复制构造函数被删除 (= delete;) 以保持指针唯一。 你展示了很多代码,但大部分都是微不足道的和不必要的。但是,您没有显示hireInternemployeeMenu 函数,这可能是罪魁祸首(因为它们是唯一与Firm 相关的函数,这是您使用unique_ptr 的唯一地方) .请展示这些功能,并可能将您的代码减少到必要的最低限度来证明问题。简而言之,请Minimal, Complete and Verifiable 举例。 @BoPersson 我在不同的主题中读到过,但我不知道出了什么问题。 @MaxLanghof 我减少了不必要的代码,并添加了hireInternemployeeMenu 函数。我希望它有助于找到解决方案。 【参考方案1】:

我认为在你的情况下你应该使用shared_ptr而不是unique_ptr,因为你在main函数、std::vector容器、hireIntern函数、employeeMenu函数和还有boost::serialization 例程。 unique_ptr 仅应在只有一个 unique_ptr 实例指向同一内存时使用 - 即它具有唯一的所有权。如果您坚持将unique_ptr 与容器一起使用并且希望将该容器传递给函数,请阅读this question 以了解如何使用移动语义(因为unique_ptr 的复制构造函数已被删除)。否则,我推荐shared_ptr

根据您的评论编辑 ====

在您提供的堆栈跟踪中,您可以找到:

/usr/include/c++/5/bits/stl_vector.h:917:30:   required from ‘void std::vector<_Tp, _Alloc>::push_back(const value_type&) [with _Tp = std::unique_ptr<Employee>; _Alloc = std::allocator<std::unique_ptr<Employee> >; std::vector<_Tp, _Alloc>::value_type = std::unique_ptr<Employee>]’
/usr/local/include/boost/serialization/vector.hpp:102:13:   required from ‘void boost::serialization::load(Archive&, std::vector<U, Allocator>&, unsigned int, mpl_::false_) [with Archive = boost::archive::text_iarchive; U = std::unique_ptr<Employee>; Allocator = std::allocator<std::unique_ptr<Employee> >; mpl_::false_ = mpl_::bool_<false>]’

您可以看到您使用类型std::vector&lt;std::unique_ptr&lt;Employee&gt;, "standard allocator"&gt; 实例化的boost::serialization 例程正在尝试执行void std::vector&lt;_Tp, _Alloc&gt;::push_back(const value_type&amp;),这对于类型std::unique_ptr 是严格禁止的。我猜这个问题出现在text_iarchive 实例化时——顾名思义,它是一个输入类,它可能在某个时候调用push_back(它很容易被确认,只需快速扫描提升代码)。

同样,如果您打算在容器中存储一个智能指针,这通常意味着您并不想为此目的使用 std::unique_ptr

【讨论】:

我阅读了您链接的问题。我已经在hireIntern 中使用emplace.back() 函数,这是一位用户建议的。它看起来像 sourceEmployee.emplace_back(new Intern(fillName, fillSurname));,它在这个函数中被调用:void hireIntern(vector&lt;unique_ptr&lt;Employee&gt;&gt;&amp; sourceEmployee)。我不知道shared_ptr boost (de)serialization implementation 应该是什么样子 调用push_back 的不是您的代码,而是提升。阅读我对答案的编辑。 你是对的。我尝试使用一次shared_ptr,但它没有用。现在,我再次用shared_ptr 替换了所有指针,一切正常!感谢您的宝贵时间。

以上是关于提升(反)序列化派生对象的向量,使用已删除的函数(unique_ptr)的主要内容,如果未能解决你的问题,请参考以下文章

在 C# 中使用派生类从 XML 反序列化对象

使用 protobuf-net 反序列化具有某些字段的派生类型的对象

将属性名称更改为在使用Json.net进行序列化时已实例化的派生类名称

使用Boost将派生类部分反序列化为基类时输入流错误

对复制构造函数已被删除的对象向量进行迭代 (C2280)

从向量 C++ 中删除对象试图引用已删除的函数