双重分派和模板类
Double dispatch and template class
我有一个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
中,我不能为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
的类型因为您可以定义一些规范的包含类型,所以可以是可行的。如果不了解Qux
和isSameSpecific
,很难说。(如果, 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来做你想做的事情,但它不会与类型多态性。
你是对的,这是双重调度,你是对的,不幸的是,一个方法不能同时是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
有点作弊(就像所有对孩子的投射一样),但是嘿它工作!
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
重写中。这样可能更安全。
- 如何将事件循环中的事件分派给订阅者?
- 将泛型子类上的检查条件分派到只知道基类的上下文中
- 带有静态分派的c++ lambda
- 通用分派器类
- 类包装器和分派器
- 如何使用SWIG为c++模板类创建分派包装器类
- 为什么在c++中调用虚函数意味着消息分派
- 带有模板参数的c++函数分派
- 在虚拟分派之后调用基成员(模拟类似虚拟析构函数的分派)
- 双重分派和模板类
- 协变返回类型和分派
- c++ for Equals()的双分派
- 在虚方法中调用虚方法时,是否应该发生虚分派
- 使用模板或宏对多个类型进行静态分派
- 不带虚表的c++动态分派
- 如何将虚拟调用从未模板化的父级干净地分派到模板化的子级
- 标记分派结构不能插入对象类型
- 具有双重分派的循环依赖
- 在不知道完整层次结构的情况下进行双重分派