6

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

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

4

4 回答 4

3

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

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

是非法的,但是:

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

不是。)

您可能可以通过创建一个 abstract QuxBaseQux<T>从中派生而侥幸逃脱。最有可能的是,这只会将问题转移到QuxBase,但如果 isSameSpecific不依赖于 的类型T,例如因为您可以定义一些规范的包含类型,那么它可能是可行的。如果不知道更多关于Quxand isSameSpecific,很难说。(如果 实例化类型不同,ifQux<T>::isSameSpecific应该总是返回false,例如,您可以键入 check in 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。如果您有其他功能受到影响,您将回到我最初所说的内容。

于 2013-04-24T08:25:10.420 回答
1

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

您可能可以使用 typeinfo 来做您想做的事,但它不会与类型多态性一起使用。

于 2013-04-24T08:12:53.133 回答
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;

于 2013-04-24T08:21:44.293 回答
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覆盖中。那可能会更安全一些。

于 2013-04-24T08:22:17.220 回答