双重调度和模板类

时间:2013-04-24 08:01:47

标签: c++ templates double-dispatch

我有一个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,我无法为isSameSpecific声明方法Qux*,因为它将是虚拟和模板。

问题:有什么方法可以解决这个问题吗?

4 个答案:

答案 0 :(得分:3)

这个问题没有真正的解决方案:你需要 每个实例化的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不依赖于Qux的类型 因为你可以定义一些规范的包含类型 是可行的。不知道更多关于isSameSpecific和。{ Qux<T>::isSameSpecific,很难说。 (如果 如果false应该始终返回QuxBase::isSameSpecific 实例化类型不同,例如,您可以键入 签入isSame,然后转到另一个 如果类型相同则为虚函数。)

请注意,类似的问题会影响所有替代方法 实现多个调度。最后,你是 要求在一组开放的类型上发送,这意味着 可能无限多种不同的功能。

编辑:

要明确一点:我假设你的getCanonicalIdentifier只是一个 例如,实际操作可能更复杂。 您显示的实际代码明显属于我的建议 第二段;事实上,它甚至可以实现 没有多次派遣。只需定义规范的“标识符” 键入,定义虚拟isSame函数,以及 在bool Foo::isSame( Foo const* other ) const { return getCanonicalIdentifier() == other->getCanonicalIdentifier(); } 中使用它:

isSame

就此而言,如果不同的类型暗示isSame 返回false(通常情况下,如果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

可能就够了。

然而,所有这些仅将 应用于{{1}}。 如果你有其他受影响的功能,那你就回来了 我最初说的话。

答案 1 :(得分:1)

编译器在解析isSameSpecific定义时必须知道(有限)class Foo个虚拟集。虚拟机都在vtable中保留了条目。模板Qux可以被无限次覆盖,在Foo中需要无限数量的虚拟。很明显,即使没有试图描述一种定义它们的方法,也无法发挥作用。

您可以使用typeinfo来执行您想要的操作,但它不适用于类型多态。

答案 2 :(得分:1)

你是对的,这是双重调度,你是对的,不幸的是,一个方法不能同时是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有点作弊(就像所有对儿童的演员一样),但嘿它的作用

Nota Bene:isSame方法可能应为const并采用const个参数,即virtual bool isSame(Foo const* foo) const;

答案 3 :(得分:1)

如何使用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覆盖中。这可能会更安全一些。