用C ++习惯方式

时间:2015-06-12 18:48:01

标签: c++ macros virtual-functions

我认为我需要的是一个纯虚函数,除了基类 有一个实现,派生类不能默认为(并且此规则必须传播)。与final相反?

我有一些来自共同基础的类型。 base是一个有用的类型,一些派生类型派生自其他派生类型。我将仅使用对基类的引用,但我需要一个virtual operator==()来查看这个并调用适合每种情况的手动比较。 operator==()的一种二维vtable。

实现不会传播到派生类是很重要的,因为不小心让这种情况发生可能会导致不兼容类型之间的比较,这些类型会落到基类实现中,而兼容类型产生假阳性。

我的意思是让特定的功能等效案例比较相同,尽管它们在不同的类别中表达。我希望问题可以扩展到其他操作,也许我在这里使用operator==()并不合适。

我没有声称知道C ++ - 我只是一个试图成为惯用语的C hack。

到目前为止,我已经解决了这个问题:

class base;
class foo;
class bar;
class baz;

#define COMPARE public: \
  virtual bool equal(base const &p) const; \
  virtual bool equal(foo const &p) const; \
  virtual bool equal(bar const &p) const; \
  virtual bool equal(baz const &p) const; \
  virtual bool operator==(base const &p) const { return p.equal(*this); }

class base {
  int a_;

 public:
  base(int a) : a_(a) {}

  COMPARE
};

class foo : public base {
  int b_;

 public:
  foo(int a, int b) : base(a), b_(b) {}

  COMPARE
};

class bar : public base {
  int c_;

 public:
  bar(int a, int c) : base(a), c_(c) {}

  COMPARE
};

class baz : public bar {
  int d_;

 public:
  baz(int a, int c, int d) : bar(a, c), d_(d) {}

  COMPARE
};

现在,感谢COMPARE,必须实现所有T::equal(),并且不允许其中任何一个都退回到早期的实现。此外,每个类都有自己的operator==(),它为自己的类型(而不是基类类型)调用适当的equal()

我想要的是以COMPARE现在的方式强制执行这些规则,但不需要记住每个派生类必须引用宏,理想情况下(要成为C ++ - 惯用)而不使用宏观。

正确的C ++方法是什么?

3 个答案:

答案 0 :(得分:3)

我还在学习,但你所描述的听起来很像它可能需要双重调度和/或访客模式。

对于双重发送,例如:

class base;
class foo;
class bar;
class baz;


class base {
    int a_;
public:
    base(int a) : a_(a) {}
    virtual bool operator==(const base&) const =0;
    virtual bool operator==(const foo&) const =0;
    virtual bool operator==(const bar&) const =0;
    virtual bool operator==(const baz&) const =0;
};

class foo : public base {
    int b_;
public:
    foo(int a,int b) : base(a),b_(b) {}
    bool operator==(const base&) const override;
    bool operator==(const foo&) const override;
    bool operator==(const bar&) const override;
    bool operator==(const baz&) const override;
};

class bar : public base {
    int c_;
public:
    bar(int a,int c) : base(a),c_(c) {}
    bool operator==(const base&) const override;
    bool operator==(const foo&) const override;
    bool operator==(const bar&) const override;
    bool operator==(const baz&) const override;
};

class baz : public bar {
    int d_;
public:
    baz(int a,int c,int d) : bar(a,c),d_(d) {}
    bool operator==(const base&) const override;
    bool operator==(const foo&) const override;
    bool operator==(const bar&) const override;
    bool operator==(const baz&) const override;
};

这看起来很像上面提到的宏选项。 :)

从TC ++ PL第4版,第22.3.1节谈到双重调度,提到或许更确切地说是使用预先计算的查找表。像

这样的东西
bool equal(const base& b1,const base& b2)
    {
        auto i = index(type_id(b1),type_id(b2));
        return intersect_tbl[i](b1,b2);
    }

答案 1 :(得分:0)

  

除了基类确实有一个实现,派生类不能默认为(并且此规则必须传播)

如果这是唯一的问题,那么Ben Voigt在评论中提到 - 这不是问题,因为可以实现纯虚函数。

  

此外,一旦派生类重写,我需要相同的规则,   更多派生类不使用覆盖(如果必须的话)   然后他们总是可以在他们自己的实现中明确地调用它)

如果是这种情况,那么“惯用的C ++方式”可能不会使用继承来模拟“派生” - “进一步派生”。继承通常用于模拟可替代性或模型多态性,但它既不在这里。换句话说:模型“派生” - 由组合“进一步衍生”。

答案 2 :(得分:0)

所以这似乎是强制派生类提供特定函数的自己的实现的一种方法:

template<typename T> struct sweep : public T {
  template <class... Args> sweep(Args&&... args) : T(args...) { }
  virtual bool equal(base const &p) const = 0;
  virtual bool equal(foo const &p) const = 0;
  virtual bool equal(bar const &p) const = 0;
  virtual bool equal(baz const &p) const = 0;
  virtual bool operator==(base const &p) const = 0;
};

class base { ... };

class foo : public sweep<base> {
  int b_;

 public:
  foo(int a, int b) : sweep(a), b_(b) {}

  ...
};

它仍然需要派生类记住做一些特定的事情来约束自己 - 使用sweep模板从基类派生 - 但它至少是C ++而不是C。

它还具有以下优点:模板可以刷新默认实现,而不是使它们成为纯虚拟实现;像这样:

template<typename T> struct sweep : public T {
  ...
  virtual bool equal(base const &p) const { return false; }
  ...
};

基于没有进一步指导的理由,每次比较都应该失败。这实际上更接近我的需要 - 但不是我要求的。