You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用C++ cereal库时派生类序列化未触发,仅基类序列化被调用

C++ cereal库多态序列化问题:派生类serialize未触发

问题描述

使用C++ cereal库时,期望实现基类自行序列化,派生类同时序列化自身及基类的功能。代码参考库文档示例,但实际运行仅基类的serialize方法被调用,派生类的未触发。

示例代码

#include <cereal/archives/json.hpp>
#include <cereal/types/polymorphic.hpp>
#include <fstream>

struct EmptyBase
{
    public:
      virtual void foo() = 0;
      std::string name;
      template <class Archive>
      void serialize( Archive & ar )
      {
        std::cout << "Base serialize" << std::endl;
        ar(name);
      }
  
    void SaveToFile(const std::string& filename) const 
    {
        std::ofstream os(filename);
        cereal::JSONOutputArchive ar(os);
        ar(cereal::make_nvp("settings", *this));
    }

    void LoadFromFile(const std::string& filename) 
    {
        std::ifstream is(filename);
        cereal::JSONInputArchive ar(is);
        ar(cereal::make_nvp("settings", *this));
    }
};

struct DerivedTwo: EmptyBase
{
  public:
    void foo() {}
    std::string why;

    template <class Archive>
    void serialize( Archive & ar )
    {
      std::cout << "Child serialize" << std::endl;
      ar(cereal::base_class<EmptyBase>(this));
      ar( why );
    }
};

CEREAL_REGISTER_TYPE(DerivedTwo)
CEREAL_REGISTER_POLYMORPHIC_RELATION(EmptyBase, DerivedTwo)

int main()
{
    DerivedTwo myclass;
    myclass.name = "test";
    myclass.why = "because";
    myclass.SaveToFile("settings.json");
}

预期输出

Child serialize
Base serialize

实际输出

Base serialize

补充需求:基类包含Save()、Load()和serialize()方法,Save()/Load()负责将类序列化/反序列化为JSON文件;派生类继承基类后,可通过调用Save()/Load()同时序列化派生类与基类的成员。

问题原因与解决方案

问题出在基类的SaveToFile和LoadFromFile方法中:直接传递*this时,由于函数是基类成员函数,*this的静态类型为EmptyBase,cereal默认按静态类型处理,不会触发多态序列化逻辑,因此只会调用基类的serialize。

要触发多态序列化,需要明确告知cereal处理多态类型,有两种可行修改方式:

方式1:使用cereal::polymorphic_cast修改序列化调用

修改基类的SaveToFile和LoadFromFile方法:

void SaveToFile(const std::string& filename) const 
{
    std::ofstream os(filename);
    cereal::JSONOutputArchive ar(os);
    // 用polymorphic_cast明确告知多态类型
    ar(cereal::make_nvp("settings", cereal::polymorphic_cast<const EmptyBase*>(this)));
}

void LoadFromFile(const std::string& filename) 
{
    std::ifstream is(filename);
    cereal::JSONInputArchive ar(is);
    ar(cereal::make_nvp("settings", cereal::polymorphic_cast<EmptyBase*>(this)));
}

方式2:使用cereal::polymorphic_wrapper包装对象

这是cereal推荐的多态序列化方式,代码修改如下:

void SaveToFile(const std::string& filename) const 
{
    std::ofstream os(filename);
    cereal::JSONOutputArchive ar(os);
    // 用polymorphic_wrapper包装多态对象
    cereal::polymorphic_wrapper<const EmptyBase> wrapper(this);
    ar(cereal::make_nvp("settings", wrapper));
}

void LoadFromFile(const std::string& filename) 
{
    std::ifstream is(filename);
    cereal::JSONInputArchive ar(is);
    cereal::polymorphic_wrapper<EmptyBase> wrapper(this);
    ar(cereal::make_nvp("settings", wrapper));
}

修改后运行代码,即可得到预期输出,派生类和基类的serialize方法都会被调用,同时满足基类提供Save/Load、派生类继承后直接使用的需求。

内容的提问来源于stack exchange,提问作者OOgaBooga

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.29 03:35:45