Reputation: 595
Hi I'm having some trouble with inhertance and operator overloading and I'm hoping you guys can give me some clarity.
I have the following classes:
template<typename Type>
class Predicate{
public:
Predicate() {};
virtual ~Predicate(){};
virtual bool operator()(const Type & value) = 0;
virtual bool operator()(const Type * value){ //<-- this is the operator thats not working
return (*this)(*value);
};
};
template<typename Type>
class Always : public Predicate<Type>{
public:
bool operator()(const Type & value){return true;}
~Always(){};
};
Now I want all my predicates to accept both references and pointers, but when I test the classes in:
int main(){
Always<int> a;
int i = 1000;
a(&i);
system("pause");
return 1;
}
I receive the following error:
test.cpp: In function 'int main()':
test.cpp:10:6: error: invalid conversion from 'int*' to 'int' [-fpermissive]
a(&i);
^
In file included from test.cpp:2:0:
predicates.h:22:7: error: initializing argument 1 of 'bool Always<Type>::operator()(const Type&) [with Type = int]' [-fpermissive]
bool operator()(const Type & value){return true;}
Upvotes: 1
Views: 116
Reputation: 27528
Templates and operator overloading obfuscate the real problem here. Look at this small piece of code which yields the same error:
void f(int &);
int main()
{
int *ptr;
f(ptr);
}
The compiler won't let you pass a pointer where a reference is expected. This is what you try to do with your derived class. As you operate on a concrete Always
, the base versions of operator()
are not considered.
Look how the situation changes when you operate instead on a pointer (or reference) to the base class:
int main(){
Predicate<int> *ptr = new Always<int>;
int i = 1000;
(*ptr)(&i);
delete ptr;
}
This compiles fine because the base-class operators are now considered for overload resolution. But this is just to make you understand the problem better. The solution is to apply the Non-Virtual Interface Idiom. Make your operators non-virtual and implement them in terms of private virtual functions:
template<typename Type>
class Predicate{
public:
Predicate() {};
virtual ~Predicate(){};
bool operator()(const Type & value) { return operatorImpl(value); }
bool operator()(const Type * value) { return operatorImpl(value); }
private:
virtual bool operatorImpl(const Type & value) = 0;
virtual bool operatorImpl(const Type * value) {
return (*this)(*value);
}
};
template<typename Type>
class Always : public Predicate<Type>{
public:
~Always(){};
private:
bool operatorImpl(const Type & value){return true;}
};
Upvotes: 0
Reputation: 76240
This is because when you are declaring:
bool operator()(const Type & value){return true;}
in the subclass, you are hiding/shadowing any other overload of the operator in the superclass.
If you add:
using Predicate<Type>::operator();
in the subclass, everything will work fine.
On a side note, I think that allowing both const&
and const*
is a design smell. You should just allow the const&
version and let the user of your class do *ptr
if they have a ptr
pointer.
Upvotes: 2