Dual Submission and Template Class

I have C ++ code where I compare different classes derived from a common mother class, Foo . If two classes do not have the same type, the comparison is always false . Otherwise, it compares some internal data related to the class.

My code is as follows:

 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... 

This works fine (I think double-submitting), I can compare Bar and Baz only with pointers to Foo .

But now there is a problem. I need to add a template class:

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

The problem is that in Foo I cannot declare the isSameSpecific method for Qux* because it will be virtual and template.

Question : is there any neat way to overcome this problem?

+6
source share
4 answers

There really is no solution to this problem: you need a isSameSpecific for each instance of the template that you use. (In other words, in Foo :

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

is illegal, but:

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

no.)

You may be able to create an abstract QuxBase , and Qux<T> derives from it. Most likely, this will simply move the problem to QuxBase , but if isSameSpecific is independent of type T , for example because you can define some canonical spanning type, it can be executable. Without knowing more about Qux and isSameSpecific , it's hard to say. (If Qux<T>::isSameSpecific should always return false if the types of instances are different, for example, you can enter check QuxBase::isSameSpecific and go to another virtual function if the types are identical.)

Please note that such problems affect all alternative methods by sending multiple times. In the end, you're requesting sending through an open set of types, which means a potentially infinite number of different functions.

EDIT:

Just to be clear: I assume that your isSame is just for example, and that the actual operations may be more complex. The actual code that you show clearly falls into what I propose in the second paragraph; in fact, it can be implemented even without multiple submissions. Just define the canonical "identifier", type, define the virtual function getCanonicalIdentifier and use this in isSame :

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

In this case, if it follows from different types that isSame returns false (often the case if isSame means that it looks like for example), you also do not need double sending:

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

The resulting isSameSpecific will have to convert the type to a pointer, but since it is guaranteed that it is the same as the this type, that is a simple and safe operation.

Finally: if classes do not have semantics of values ​​(and almost certainly should not, if polymorphism is involved), something simple:

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

may be enough.

All this applies only to something like isSame . If you have other features that are affected, you are back to what I originally said.

+3
source

The compiler must know the (final) set of virtual isSameSpecific while it parses the definition of class Foo . All virtual all reserved entries in the vtable. A Qux template could be redefined an unlimited number of times, requiring an unlimited number of virtual machines in Foo . Obviously, this is impossible without even trying to describe a method for determining them.

You can probably use typeinfo to accomplish what you want, but this is not a type of polymorphism.

+1
source

You are right that this is a double dispatch, and you are right that, unfortunately, the method cannot be both virtual and template (the last one is an implementation problem).

I am afraid that there is no way to do this with a clean design; however you can spoof 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 

Of course, dynamic_cast little deceiving (like all casts to children), but hey it works!

Nota Bene: isSame methods should probably be const and take arguments const , aka virtual bool isSame(Foo const* foo) const;

+1
source

How about using 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); } 

Alternatively, you can put typeid code in each compare_with_same override. It might be a little safer.

+1
source

Source: https://habr.com/ru/post/943511/


All Articles