我有一个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...
这很有效(我认为这是双重调度),我可以将Bar
和Baz
仅与Foo
的指针进行比较。
但现在出现了问题。我必须添加一个模板类:
template< typename T>
class Qux : public Foo
{
//...
};
问题在于Foo
,我无法为isSameSpecific
声明方法Qux*
,因为它将是虚拟和模板。
问题:有什么方法可以解决这个问题吗?
答案 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)
你是对的,这是双重调度,你是对的,不幸的是,一个方法不能同时是virtual
和template
(后者是一个实现问题)。
我担心纯设计不可能这样做;但你可以在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
覆盖中。这可能会更安全一些。