I have a Class structure as follows:
class Base {
public:
void setDefault( uint8_t my_default ) { m_default = my_default; }
void method( uint8_t * subject ) { method( subject, m_default ); }
virtual void method( uint8_t * subject, uint8_t parameter ) =0;
protected:
uint8_t m_default;
};
class Derived1 : public Base {
public:
void method ( uint8_t * subject, uint8_t parameter ) { /* do something to subject */ }
};
class Derived2 : public Base {
public:
void method ( uint8_t * subject, uint8_t parameter ) { /* do something different to subject */ }
};
I want to be able to call method( ... )
on any instance of a class derived from Base
, which will call the method defined in the derived class using the member variable as the default parameter.
From what I've read elsewhere on Stack Overflow here, the override must have the same signature, which is why it won't compile.
Are there any potential ambiguities with this approach?
Two ways I can think to get around this:
- declare a default
method( void )
for each derived class, but this doesn't seem very DRY - use a different name for the default method (e.g.
defaultMethod( uint8_t * subject )
) but I feel this makes my class less intuitive
Is there a better way around this?
Here is a complete example, which won't compile (Arduino IDE 1.7.9):
class Base {
public:
void setDefault( uint8_t my_default ) { m_default = my_default; }
void method( uint8_t * subject ) { method( subject, m_default ); }
virtual void method( uint8_t * subject, uint8_t parameter ) =0;
protected:
uint8_t m_default;
};
class Derived1 : public Base {
public:
void method ( uint8_t * subject, uint8_t parameter ) { *subject += parameter; }
};
class Derived2 : public Base {
public:
void method ( uint8_t * subject, uint8_t parameter ) { *subject *= parameter; }
};
Derived1 derived1;
Derived2 derived2;
uint8_t subject = 0;
void setup() {
// put your setup code here, to run once:
derived1.setDefault( 3 );
derived2.setDefault( 5 );
}
void loop() {
// put your main code here, to run repeatedly:
derived1.method( &subject, 1 ); // subject == 1
derived2.method( &subject, 2 ); // subject == 2
// won't compile with this line:
derived1.method( &subject ); // subject == 5
}
Error produced is:
over-ride-load.ino: In function 'void loop()':
over-ride-load.ino:39:29: error: no matching function for call to 'Derived1::method(uint8_t*)'
over-ride-load.ino:39:29: note: candidate is:
over-ride-load.ino:14:12: note: virtual void Derived1::method(uint8_t*, uint8_t)
over-ride-load.ino:14:12: note: candidate expects 2 arguments, 1 provided
Error compiling.
What I believe that you're looking for is the
using
directive.You've done everything correctly in
Base
. While using a default parameter is (often) better than defining a second function, given your requirements (using a member) that's not possible here: so you defined a second overloaded function to fix it (and defined itinline
- kudos!).But the problem comes with the derived classes. If you didn't override the virtual function, everything would have been fine: both versions of
method
would be available to you. But you have to override it, so you effectively "mask" the base version ofmethod(subject);
withmethod(subject,parameter);
.What you want to do is "promote" all of
Base
'smethod
s into the variousDerived
s, to give them equal weighting. How? With theusing
directive.In each
Derived
definition, put the following code:That "promotes" all of
Base
's method into theDerived
- while still allowing you to override individual ones. I suggest that you put that line directly above eachDerived
'smethod()
override.It will compile (if you fix the syntax errors). It will compile because the override does have the same signature:
versus
On the other hand, option 2. (different name in favour of overload) still sounds appealing. Overloads can sometimes be tricky, confusing and counter intuitive.
However, even though your example is correct, what you might find is that following does not work, which might be counter intuitive:
This is because of how overload resolution works. Your suggested option 2. trivially resolves this issue, and that is why I'm recommending you to do that. Other ways to call the method in parent:
d2.Base::method(&b);
using Base::method;
declaration to each derived class. This is described more in depth in John Burger's answer.