Reputation: 2741
I'm trying to create a non-template base class to create an interface through which I can interact with derived template classes. My hope is to use partial virtual function overloads, but something isn't working, and I'm not sure why. Can anyone explain why I the below code returns:
B match = False
D match = True
E match = False
instead of B,D returning True, and E returning False? I would have thought that the overloaded operator in the derived class would pick up the pointer-to-int '&i' and be called, but it doesn't.
To be clear, I'm not trying to -override- the Base version of match, I am trying to -overload- it, specifically wanting it to have a different, in this case more specialized, interface than the one in Base, which takes over when its function signature is used.
I'm also trying to avoid having to extend the Base class for every flavor of the Derived template that I might instantiate.
Even stranger, and I -might- just be going crazy here, but I swear this was working for me at some point not too long ago! Fwiw I'm on OSX 10.5, using llvm 7.0.0 and clang 700.1.76. Could this be a compiler idiosyncracy?
While I am attempting (unsuccessfully) to use partial overloads here, I'm really open to any approach that solves the problem of picking a template instance function by the type of its arguments, without proliferating classes, if-thens/case or adding specific specializations to the Base class. I'd love to hear it if you have another approach that I could drop in for similar functionality.
Thank you for any insights you can offer!
#include <stdio.h>
class Base
{
public:
Base() {}
virtual ~Base(){}
virtual bool match( const void *data ) const { return false; }
};
template <class Type>
class Derived: public Base
{
public:
Derived():Base() {}
~Derived() override{}
virtual bool match( const Type *data ) const { return true; }
};
int main(int argc, char **argv)
{
Derived<int> *d = new Derived<int>();
Derived<float> *e = new Derived<float>();
Base *b = d;
int i;
printf("B match = %s\n",b->match(&i)?"True":"False");
printf("D match = %s\n",d->match(&i)?"True":"False");
printf("E match = %s\n",e->match(&i)?"True":"False");
}
Upvotes: 3
Views: 1640
Reputation: 2741
Thanks to o_weisman's comment under the original question, I was able to find a working solution using function templates, (see below). Admittedly in my solution's current form, I leverage a restriction that each Type-instance of Derived is a singleton. This works well for my particular design, but the behavior could be expanded as needed. One possibility that would allow for multiple instances of Derived might be to check if the 'this' pointer in Base::match is in a set of all instances (tracked in a static set variable updated at construct/destruct time), instead of against a single singleton instance. Anyhow, I hope this helps someone who might be facing a similar design challenge.
#include <stdio.h>
#include <assert.h>
template <class Type> class Derived;
class Base
{
public:
Base() {}
virtual ~Base(){}
template <class Type>
bool match( const Type *data ) const { return (Derived<Type>::instance() == this); }
};
template <class Type>
class Derived: public Base
{
public:
Derived(): Base() { assert(!ourInstance); ourInstance = this; }
~Derived() override{}
static const Base *instance() { return ourInstance; }
protected:
static const Derived<Type> *ourInstance;
};
template <class Type>
const Derived<Type> *Derived<Type>::ourInstance = NULL;
int main(int argc, char **argv)
{
Derived<int> *d = new Derived<int>();
Derived<float> *e = new Derived<float>();
Base *b = d;
int i;
printf("B match = %s\n",b->match(&i)?"True":"False");
printf("D match = %s\n",d->match(&i)?"True":"False");
printf("E match = %s\n",e->match(&i)?"True":"False");
}
This produces the desired result of:
B match = True
D match = True
E match = False
Upvotes: 1
Reputation: 206567
If you were to manually create Derived<int>
as a class, its member function would be:
virtual bool match( const int *data ) const { return true; }
It doesn't override the base class. Hence, when you invoke the function with base class pointer, it executes the base class implementation and when you invoke it with the derived class pointer, it executes the derived class implementation.
You will be able to catch the problem at compile time if you use the override
keyword.
template <class Type>
class Derived: public Base
{
public:
Derived():Base() {}
~Derived() override{}
virtual bool match( const Type *data ) const override { return true; }
};
You should see a compile time error with that change.
See the compiler error at http://ideone.com/8rBQ6B.
Update, in response to OP's comment
If you don't mean to override:
virtual
in the member function declaration.Bring the base class function into the scope of the derived class by using
using Base::match
Here's how to do it:
template <class Type>
class Derived: public Base
{
public:
Derived():Base() {}
~Derived() override{}
using Base::match;
bool match( const Type *data ) const { return true; }
};
Upvotes: 3
Reputation: 1144
The reason this doesn't work is because the derived class doesn't actually override the virtual function in the base class. In order to override a virtual function in a base class a derived class must have exactly the same signature (see note below for an exception). In this case the signatures are different because the virtual function in the base class takes a void *
but the function in the derived class takes a type *
.
Let's make one small change and add the override
directive to the function signature in the derived class:
#include <stdio.h>
class Base
{
public:
Base() {}
virtual ~Base(){}
virtual bool match( const void *data ) const { return false; }
};
template <class Type>
class Derived: public Base
{
public:
Derived():Base() {}
~Derived() override{}
virtual bool match( const Type *data ) const override { return true; }
};
int main(int argc, char **argv)
{
Derived<int> *d = new Derived<int>();
Base *b = d;
int i;
printf("B match = %s\n",b->match(&i)?"True":"False");
printf("D match = %s\n",d->match(&i)?"True":"False");
}
Here's what happens when we compile now:
main.cpp: In instantiation of 'class Derived<int>':
main.cpp:24:38: required from here
main.cpp:19:16: error: 'bool Derived<Type>::match(const Type*) const [with Type = int]' marked 'override', but does not override
virtual bool match( const Type *data ) const override { return true; }
Assuming we're using C++11 it's always a good idea to use override
to be sure we really are overriding a base class virtual function.
Note from above: There's something called a covariant return type where an override doesn't have to have the same signature, but that's beyond the scope of the question.
Upvotes: 1
Reputation: 1775
because the signatures of the functions do not match:
In class Base
you have
virtual bool match( const void *data ) const { return false; }
note the const void *
parameter type
then in Derived
you have
virtual bool match( const Type *data ) const { return true; }
Where Type
in this instance is int
(from main
's Derived<int> *d
)
Just add a virtual in Base with a const int* data
signature, then you'll be good to go.
And yes, that does mean that in Base
you'll have to add an overload of match
for every Type
you expect to use with Derived
.
Upvotes: 1
Reputation: 180415
The problem here is that Derived::match
does not match Base::match
. The function signatures of virtual functions have to be the same for the mechanism to work. So with your code Derived::match
overloads instead of overriding it.
If we change
virtual bool match( const Type *data ) const { return true; }
To
virtual bool match( const void *data ) const { return true; }
Then we get
B match = True
D match = True
Upvotes: 1