【问题标题】:Virtual functions and template clash虚函数和模板冲突
【发布时间】:2012-05-07 22:56:38
【问题描述】:

我有一个 pointAccumulator 的抽象基类。这个抽象基础将填充有诸如返回所有点平均值的函数之类的方法。这两个类的示例如下所示:

class lala {
public:
    virtual someFunctions = 0;

    virtual bool isEmpty() = 0;
};


class lalaLower : public lala {
public:
    lalaLower(){}
    ~lalaLower(){}


    someFunctions

    template<class Archive> void serialize(Archive & ar, const unsigned int version) {
        ar & heights_;
    }

protected:
    std::deque<double> heights_;
};

正如您在代码中看到的,我还想使用 boost 序列化来保存这些类型。现在使用工厂模式,我相信您可以像这样调用 pointAccumulator 类型:

lala *a1 = new lalaLower();

我的问题是,如果我这样调用模板化的序列化方法,它将无法访问。我也不能在抽象类中使用模板类,因为 c++ 不允许这样做。有没有办法解决这个问题?

编辑:

我已经考虑过序列化的非侵入式方法,但这需要公开 heights_ ,这并不理想,也不是良好的编程风格。我认为潜在的使用友元类或函数的方法可以通过访问变量来穿透类,同时仍然保持基类抽象?谁能解释一下这是如何工作的?

【问题讨论】:

  • 这是一个根本问题;虚函数是关于基于类型的运行时选择;模板是关于基于类型的编译时代码生成;如果直到运行时才知道类型,则无法在编译时生成代码。
  • 是的,我在问题中说明了这一点。所以我想现在的问题是,有没有更好或不同的方法来解决它?
  • 我相信这是“Andrei Alexandrescu 的现代 C++ 设计”一书中讨论的基本主题。如果您想很好地理解这一点,请阅读。

标签: c++ templates abstract-class boost-serialization


【解决方案1】:

我认为使用友元类或函数是一个很好的解决方案,您可以添加新的类,例如 Serializor

这里是友元函数的一个例子

class Serializor;
class meanAccumulator : public pointAccumulator 
{ 
public:     
meanAccumulator(){}     
~meanAccumulator(){}     
double getHeight();     
void addHeight(double Height);     
void setHeight(double Height);     
bool isEmpty(){ return heights_.empty(); }      

protected:     std::deque<double> heights_; 
friend int Serializor::Func1( Serializor& );

};

参考http://msdn.microsoft.com/en-us/library/ahhw8bzz.aspx

【讨论】:

    【解决方案2】:

    我相信如果不以这种或其他方式将 Archive 类型参数包装到多态层次结构中,您将无法做到这一点。

    似乎Boost.Serializationdoes it for you

    【讨论】:

    • +1 如果 Boost 没有想到并解决了这个问题,那将是非常令人惊讶的!
    • 我对这个解决方案有点困惑。它确实说使用多态方法不再需要模板。但是,在给定的示例中,demo_polymorphic_A.cpp 和相关的 h 文件,类 A(即被序列化的类)。无论如何使用模板序列化?编辑:我明白了,模板仍然存在,但他们使用 cpp 文件显式实例化它。作为一个问题,我如何使用它来解决我的问题我是否仍然需要在我的抽象类中使用模板函数来使用这个解决方案?
    • 我猜这些例子本身实际上已经坏了。
    • 不,我不认为他们是,他们可能只是为了解决这个问题而不是延迟编译时间或类似的问题?
    • 在这种情况下,您将面临一个可怕的选择,即为存档类型参数实现自己的层次结构。
    【解决方案3】:

    您可能知道templates 和virtual 方法并不能齐头并进。

    我建议删除meanAccumulator::serialize() 方法并添加到class pointAccumulator 主体中,并在需要它们的地方调用virtual 函数。
    您还可以考虑传递派生类的句柄并使用它调用方法。

    class pointAccumulator {
    public:
      template<class Archive, class Derived>
      void serialize(Archive & ar, const unsigned int version, Derived &derived)
      {                                            // optional ^^^^^^^^^^^^^^^^^
        // call virtual methods to derived from here
        // optional: for non-virtual method, you can use derived class handle
      }
    };
    

    您唯一需要注意的是,无论您在 serialize() 中使用 derived 句柄调用的任何非虚拟方法 --> 在来自 pointAccumulator 的所有子类中都应具有相同的名称,无论他们在里面做什么。

    【讨论】:

    • 我希望保持类 pointAccumulator 纯粹是虚拟的,这样工厂模式仍然可以工作。我也需要序列化。你有解决方案吗?
    • @Ben,如果你保留一些非虚拟功能有什么问题? pointAccumulator 仍然是抽象的。我觉得我建议的方式会让事情变得更容易。我没有找到其他方法。
    • 我会试一试,但我认为对于工厂类来说,拥有纯虚函数是更好的编程技术。我真的希望让它保持虚拟。
    • @Ben,我相信您应该采用更适合您需求的技术。这就是为什么 c++ 比意识形态更灵活的原因。:)
    【解决方案4】:

    事实上,我会把我的评论作为答案:

    ~/src/snips$ cat serializer-demo.cc 
    #include <boost/archive/polymorphic_iarchive.hpp>
    #include <boost/archive/polymorphic_oarchive.hpp>
    
    typedef boost::archive::polymorphic_iarchive bpi;
    typedef boost::archive::polymorphic_oarchive bpo;
    typedef const unsigned int cui;
    
    struct ABC
    {
            virtual void serialize(bpi &ar, cui v) = 0;
            virtual void serialize(bpo &ar, cui v) = 0;
    };
    
    struct A : ABC
    {
            void serialize(bpi &ar, cui v ) { ar & data; }
            void serialize(bpo &ar, cui v ) { ar & data; }
            int data;
            bool operator==(const A & rhs) const
                    { return data == rhs.data; }
            A(int data=0) : data(data) {}
    };
    
    #include <sstream>
    #include <boost/archive/polymorphic_text_iarchive.hpp>
    #include <boost/archive/polymorphic_text_oarchive.hpp>
    
    #include <boost/archive/polymorphic_binary_iarchive.hpp>
    #include <boost/archive/polymorphic_binary_oarchive.hpp>
    int main(int argc, char* argv[])
    {
        const A a(1);
        const ABC &abc = a;
        A a1;
        ABC &abc1 = a1;
        {
            std::stringstream ss;
            {
                boost::archive::polymorphic_text_oarchive oa(ss);
                boost::archive::polymorphic_oarchive & oa_interface = oa; 
                oa_interface << abc;
            }
            {
                boost::archive::polymorphic_text_iarchive ia(ss);
                ia >> abc1;
            }
        }
        if(! (a == a1))
            return 1;
        {
            std::stringstream ss;
            {
                boost::archive::polymorphic_binary_oarchive oa(ss);
                oa << abc;
            }
            {
                boost::archive::polymorphic_binary_iarchive ia(ss);
                boost::archive::polymorphic_iarchive & ia_interface = ia; 
                ia_interface >> abc1;
            }
        }
        if(! (a == a1))
            return 1;
        return 0;
    }
    
    ~/src/snips$ make -B serializer-demo
    g++ -o bin/serializer-demo --std=c++0x -g -O -march=native -pipe -Wall -Wno-parentheses   -lboost_serialization  serializer-demo.cc
    ~/src/snips$ type -pa serializer-demo
    ./bin/serializer-demo
    ~/src/snips$ serializer-demo
    ~/src/snips$ echo $?
    0
    ~/src/snips$ 
    

    【讨论】:

    • 感谢您的回答,如果序列化方法将在使用常规模板化方法的更高类序列化内部调用,有没有办法使用它?目前我收到很多编译错误,因为模板“存档”默认设置为 text_iarchive。
    • @Ben 非模板化序列化采用多态 {i,o} 归档,所以这就是您必须提供的。您可以在流上构建档案,我即将和我的孩子一起玩游戏,因此您可以寻找是否可以随意在其他档案上构建档案并让它们以您想要的方式混合。
    • 我在构建代码时使用了 polymorphic_binary_(i/o)archives,但它仍然编译说 text_(i/o)archive 和 binary_(i/o)archive 没有匹配的函数每次我使用该方法。
    • 看看上面boost::archive::polymorphic_iarchive &amp; ia_interface = ia; ia_interface &gt;&gt; abc1; 这样的行:示例代码已经在做你想要的。
    • 我现在已经正确实现了它并且构建良好。但是当我运行它时它说未注册的类-未注册或导出的派生类。如果我尝试导出它,我会得到上面提到的所有疯狂的编译错误。
    【解决方案5】:

    所以我有一种有趣的方式来为模板化函数伪造虚拟化。 Faking a virtual templated function c++

    如果您需要在层次结构中强制执行此操作,则此处可能会应用基本动机,但您可以利用 boost 元编程库来为该问题提供运行时解决方案。

    【讨论】:

      猜你喜欢
      • 2013-12-31
      • 2015-12-08
      • 2015-10-07
      • 2021-01-04
      • 2012-07-03
      • 1970-01-01
      • 2022-07-20
      • 1970-01-01
      • 2010-11-19
      相关资源
      最近更新 更多