C++ 如果我没有';I don’我事先不知道所有的课吗?

C++ 如果我没有';I don’我事先不知道所有的课吗?,c++,inheritance,typeid,double-dispatch,extensible,C++,Inheritance,Typeid,Double Dispatch,Extensible,我有一个基类(可能)有很多子类,我希望能够比较基类的任意两个对象是否相等。我试图在不调用亵渎的typeid关键字的情况下实现这一点 #include <iostream> struct Base { virtual bool operator==(const Base& rhs) const {return rhs.equalityBounce(this);} virtual bool equalityBounce(const Base*

我有一个基类(可能)有很多子类,我希望能够比较基类的任意两个对象是否相等。我试图在不调用亵渎的typeid关键字的情况下实现这一点

#include <iostream>

struct Base {

    virtual bool operator==(const Base& rhs) const
        {return rhs.equalityBounce(this);}

    virtual bool equalityBounce(const Base* lhs) const = 0;
    virtual bool equalityCheck(const Base* lhs) const = 0;
};

struct A : public Base {

    A(int eh) : a(eh) {}
    int a;

    virtual bool equalityBounce(const Base* lhs) const{
        return lhs->equalityCheck(this);
    }

    virtual bool equalityCheck(const Base* rhs) const {return false;}
    virtual bool equalityCheck(const A* rhs) const {return a == rhs->a;}
};


int main() {

    Base *w = new A(1), *x = new A(2);

    std::cout << (*w == *w) << "\n";
    std::cout << (*w == *x) << "\n";
}
#包括
结构基{
虚拟布尔运算符==(常量基和rhs)常量
{返回rhs.equalityBounce(this);}
虚拟布尔均衡反弹(常数基*lhs)常数=0;
虚拟布尔均衡检查(常数基*lhs)常数=0;
};
结构A:公共基础{
A(int-eh):A(eh){}
INTA;
虚拟布尔均衡反弹(常数基*lhs)常数{
返回lhs->equalityCheck(此);
}
虚拟布尔均衡检查(const Base*rhs)const{return false;}
虚拟布尔均衡检查(常量A*rhs)常量{返回A==rhs->A;}
};
int main(){
基准*w=新A(1),*x=新A(2);

std::cout如果您需要一个virtual equals操作符,并且希望避免动态强制转换,那么您可以向基类添加一个指示类型的枚举类型,并生成一个虚拟getter,然后在匹配的情况下执行静态强制转换

enum ClassType { AType, BType };
Class A 
{
public:

virtual ClassType getType ();
virtual bool operator ==(const A& a ) const;
};

// in B implementation

virtual bool B::operator ==(const A& a)
{
if (!A::operator==(a)) return false;
if(a.getType () != ClassType::BType) return false;
const B& b = static_cast <const& B>(a);
// do B == checks
return true;
}
enum ClassType{AType,BType};
甲级
{
公众:
虚拟类类型getType();
虚拟布尔运算符==(常数A&A)常数;
};
//在B执行中
虚拟布尔B::运算符==(常量A和A)
{
如果(!A::operator==(A))返回false;
if(a.getType()!=ClassType::BType)返回false;
常数B&B=静态(a);
//B=支票吗
返回true;
}

为什么它不起作用

双重分派实现的问题在于,您希望调用最具体的
equalityCheck()

但是您的实现完全基于多态基类,
equalityCheck(const a*)
重载,但不会覆盖
equalityCheck(const base*)

换句话说,在编译时编译器知道
A::equalityBounce()
可以调用
equalityCheck(A*)
(因为
这个
是一个
A*
),但不幸的是,它调用了
Base::equalityCheck()
,而这个
A*
参数没有专门的版本

如何实施它?

为了使双调度工作,您需要在基类中具有双调度equalityCheck()的特定于类型的实现

要使其工作,基地需要了解其后代:

struct A; 

struct Base {

    virtual bool operator==(const Base& rhs) const
    {
        return rhs.equalityBounce(this);
    }

    virtual bool equalityBounce(const Base* lhs) const = 0;
    virtual bool equalityCheck(const Base* lhs) const = 0;
    virtual bool equalityCheck(const A* lhs) const = 0;
};

struct A : public Base {
    ...
    bool equalityBounce(const Base* lhs) const override{  
        return lhs->equalityCheck(this);
    }
    bool equalityCheck(const Base* rhs) const override {
        return false; 
    }
    bool equalityCheck(const A* rhs) const override{
        return a == rhs->a;
    }
};
注意使用
override
以确保函数确实覆盖了基函数的虚拟函数

通过此实施,它将起作用,因为:

  • A::equalityBounce()
    将调用
    Base::equalityCheck()
  • 在该函数的所有重载版本中,它将选择
    Base::equalityCheck(A*)
    ,因为
    this
    A*
  • 被调用的
    Base*lhs
    对象将调用它的
    equalityCheck(A*)
    。如果
    lhs
    A*
    对象,它将因此选择
    A::equalityCheck(A*)
    ,这将产生预期的(正确的)结果。恭喜!
  • 假设
    lhs
    将是指向另一个类
    X
    的指针,该类也派生自
    Base
    。在这种情况下,
    lhs->equalityCheck(a*)
    将调用
    X::equalityCheck(a*)
    并且还可以返回正确的响应,考虑到将
    X
    A
    进行比较
如何使其可扩展?双重分派映射!

使用强类型语言进行双重分派的问题是,“反弹”对象需要知道如何与特定对象进行比较(预先知道)类。由于源对象和反弹对象具有相同的多态基类型,因此基需要知道所有涉及的类型。这种设计严重限制了可扩展性

如果您希望能够添加任何派生类型,而不必事先在基类中知道它,那么必须遍历动态类型(无论是dynamic\u cast还是typeid):

我在这里向您提出了一个动态扩展性的建议。它使用单个分派来比较相同类型的两个对象,并使用双分派映射来比较它们之间的不同类型(如果未声明任何内容,则默认返回false):

然后,为了使动态双调度工作,我必须将此函数添加到双调度映射中:

Base::tcmp[typeid(X)][typeid(A)] = iseq_X_A;
然后,结果很容易验证:

Base *w = new A(1), *x = new A(2), *y = new X(2);
std::cout << (*w == *w) << "\n";  // true returned by A::equalityStrict
std::cout << (*w == *x) << "\n";  // false returned by A::equalityStrict 
std::cout << (*y == *x) << "\n";  // true returned by isseq_X_A
Base*w=新的A(1),*x=新的A(2),*y=新的x(2);

std::cout虽然由于
动态播放的原因速度较慢,但我认为最舒适的解决方案是下一个:

#include <iostream>

struct Base {
    virtual bool operator==(const Base& rhs) const
    { return equalityBounce(&rhs); }

    virtual bool equalityBounce(const Base* lhs) const = 0;
};

template<typename Derived>
struct BaseHelper : public Base
{
    bool equalityBounce(const Base* rhs) const
    {
        const Derived* p_rhs = dynamic_cast<const Derived*>(rhs);

        if (p_rhs == nullptr)
            return false;
        else
            return p_rhs->equalityCheck
             (reinterpeter_cast<const Derived*>(this));
    }

    virtual bool equalityCheck(const Derived*) const = 0;
};

struct A : public BaseHelper<A> {

    A(int eh) : a(eh) {}
    int a;

    virtual bool equalityCheck(const A* rhs) const
    { return a == rhs->a; }
};


int main() {

   Base *w = new A(1), *x = new A(2);

   std::cout << (*w == *w) << "\n"; // Prints 1.
   std::cout << (*w == *x) << "\n"; // Prints 0.
}
#包括
结构基{
虚拟布尔运算符==(常量基和rhs)常量
{返回equalityBounce(&rhs);}
虚拟布尔均衡反弹(常数基*lhs)常数=0;
};
模板
结构BaseHelper:公共基
{
布尔均衡反弹(常数基*rhs)常数
{
常量导出*p_rhs=动态(rhs);
如果(p_rhs==nullptr)
返回false;
其他的
返回p_rhs->equalityCheck
(赖因特·彼得·奥卡斯特(本));
}
虚拟布尔均衡检查(常量派生*)常量=0;
};
结构A:公共BaseHelper{
A(int-eh):A(eh){}
INTA;
虚拟布尔均衡检查(常数A*rhs)常数
{返回a==rhs->a;}
};
int main(){
基准*w=新A(1),*x=新A(2);

老实说,我认为您需要一个外部等式检查器——一组重载的自由函数(如果您愿意,也可以绑定到类中)知道如何比较各种类型。当你添加一个新类型时,你需要添加一些新函数。@Pradhan的可能重复注意:@Pradhan不是另一个SO问题的完全重复。这两个问题都与双重分派的equal有关。这一个引入了可扩展性约束t
Base::tcmp[typeid(X)][typeid(A)] = iseq_X_A;
Base *w = new A(1), *x = new A(2), *y = new X(2);
std::cout << (*w == *w) << "\n";  // true returned by A::equalityStrict
std::cout << (*w == *x) << "\n";  // false returned by A::equalityStrict 
std::cout << (*y == *x) << "\n";  // true returned by isseq_X_A
#include <iostream>

struct Base {
    virtual bool operator==(const Base& rhs) const
    { return equalityBounce(&rhs); }

    virtual bool equalityBounce(const Base* lhs) const = 0;
};

template<typename Derived>
struct BaseHelper : public Base
{
    bool equalityBounce(const Base* rhs) const
    {
        const Derived* p_rhs = dynamic_cast<const Derived*>(rhs);

        if (p_rhs == nullptr)
            return false;
        else
            return p_rhs->equalityCheck
             (reinterpeter_cast<const Derived*>(this));
    }

    virtual bool equalityCheck(const Derived*) const = 0;
};

struct A : public BaseHelper<A> {

    A(int eh) : a(eh) {}
    int a;

    virtual bool equalityCheck(const A* rhs) const
    { return a == rhs->a; }
};


int main() {

   Base *w = new A(1), *x = new A(2);

   std::cout << (*w == *w) << "\n"; // Prints 1.
   std::cout << (*w == *x) << "\n"; // Prints 0.
}