双重分派和模板类

Double dispatch and template class

本文关键字:分派      更新时间:2023-10-16

我有一个c++代码,在那里我比较从一个共同的母类Foo派生的不同类。如果两个类的类型不相同,则总是比较false。否则,它比较一些特定于类的内部数据。

我的代码是这样的:

class Bar;
class Baz;
class Foo
{
public:
    virtual bool isSame( Foo* ) = 0;
    virtual bool isSameSpecific( Bar* ){ return false; }
    virtual bool isSameSpecific( Baz* ){ return false; }
};
class Bar : public Foo
{
public:
    bool isSame( Foo* foo){ return foo->isSameSpecific(this); }
    bool isSameSpecific( Bar* bar){ return bar->identifier == identifier; }
    int identifier;
};
// and the same for Baz...

这工作得很好(我认为这是一个双重调度),我可以比较BarBaz只有指向Foo的指针。

但是现在问题来了。我必须添加一个模板类:

template< typename T>
class Qux : public Foo
{
//...
};

问题是,在Foo中,我不能为Qux*声明方法isSameSpecific,因为它将是虚拟的和模板的。

问题:有没有什么简洁的方法来克服这个问题?

这个问题没有真正的解决方案:你需要的每个实例化一个isSameSpecific函数您使用的模板。(换句话说,在Foo:

template <typename T>
virtual bool isSameSpecific( Qux<T>* );

是非法的,但是:

virtual bool isSameSpecific( Qux<int>* );
virtual bool isSameSpecific( Qux<double>* );
//  etc.

不是。)

你也许可以创建一个抽象QuxBase,并有Qux<T>从它派生。最有可能的是,这只会把问题转移到QuxBase,但是如果例如,isSameSpecific不依赖于T的类型因为您可以定义一些规范的包含类型,所以可以是可行的。如果不了解QuxisSameSpecific,很难说。(如果, Qux<T>::isSameSpecific应该总是返回false实例化类型是不同的,例如,您可以键入检查QuxBase::isSameSpecific,并转发给另一个如果类型相同,则为虚函数。)

请注意,类似的问题会影响到的所有替代方法也实现了多重分派。最后,你是请求调度一组开放类型,也就是说可能有无穷多个不同的函数

编辑:

只是为了清楚:我假设你的isSame只是一个举例来说,实际操作可能会更复杂。您所展示的实际代码显然属于我建议的范围第二段;事实上,它甚至可以实现无需多重调度。只要定义一个规范的"标识符"类型,定义一个虚getCanonicalIdentifier函数,和使用isSame:

bool Foo::isSame( Foo const* other ) const
{
    return getCanonicalIdentifier() 
        == other->getCanonicalIdentifier(); 
}

对于这个问题,如果不同的类型意味着isSame返回false(通常情况下,如果isSame意味着它看起来的样子)像)一样,你也不需要双重分派:

bool Foo::isSame( Foo const* other ) const
{
    return typeid( *this ) == typeid( *other )
        && isSameSpecific( other );
}

派生的isSameSpecific必须转换的类型指针,但因为它们保证是相同的作为this的类型,这是一个简单而安全的操作。

最后:如果类没有值语义(和几乎肯定不应该,如果涉及多态性),就像:

bool Foo::isSame( Foo const* other ) const
{
    return this == other;
}

可能就足够了。然而,所有这些只适用于之类的isSame。如果你有其他功能受到影响,你就回来

编译器在解析class Foo定义时必须知道isSameSpecific虚函数的(有限)集合。虚函数在虚表中都有保留的表项。模板Qux可以被无限次覆盖,这就要求Foo中有无限数量的虚拟机。显然,这是行不通的,即使没有尝试描述一种定义它们的方法。

你可以使用typeinfo来做你想做的事情,但它不会与类型多态性。

你是对的,这是双重调度,你是对的,不幸的是,一个方法不能同时是virtualtemplate(后者是一个实现问题)。

纯粹的设计恐怕不可能做到这一点;但是你可以在Qux中作弊。

template <typename T>
class Qux: public Foo {
    virtual bool isSame( Foo* foo ) {
        if (Qux* q = dynamic_cast<Qux*>(foo)) {
            return *this == *q;
        }
        return false;
    }
}; // class Qux

当然,dynamic_cast有点作弊(就像所有对孩子的投射一样),但是嘿它工作!

注意:isSame方法可能应该是const,并接受const参数,即virtual bool isSame(Foo const* foo) const;

如何使用RTTI:

#include <typeinfo>
struct Foo
{
    virtual ~Foo() { }
    virtual bool compare_with_same(Foo const & rhs) = 0;
};
struct Bar : Foo
{
    int thing;
    virtual bool compare_with_same(Foo const & rhs)
    {
        assert(dynamic_cast<Bar const *>(&rhs) != nullptr);
        return static_cast<Bar const &>(rhs).thing == thing;
    }
}
bool operator==(Foo const & lhs Foo const & rhs)
{
    return typeid(lhs) == typeid(rhs) && lhs.compare_with_same(rhs);
}

或者,您可以将typeid代码放入每个compare_with_same重写中。这样可能更安全。