Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Question on inheritence and function overloading.

0 views
Skip to first unread message

xx

unread,
Jun 20, 2003, 2:36:33 PM6/20/03
to
I have been using C++ for well over a decade and this is something that
I am seeing that I can't find an explanation for:

typedef unsigned char Boolean;

class Inh1
{
public:

virtual int foobar(void);
virtual Boolean Test(void *t, Boolean b);
virtual Boolean Test(int x, void *t, void *u);
};

class Inh2 : public Inh1
{
public:
Boolean Test(void *t, unsigned char b);
};

int funct()
{
Inh2 i;

i.foobar();
i.Test(25, (void *)0, (void *)0);
return 0;
}

In the above example, Inh1 declares two functions which are overloaded.
Inh2 is a class which overides the implementation of one of the
overloaded function, but when the other is to be used, the compiler does
not see the overloaded function.

Obviously, foobar() gets carried into the definition of Inh2, why
doesn't the overloaded version of Test?

Robert W Hand

unread,
Jun 20, 2003, 4:15:41 PM6/20/03
to
On Fri, 20 Jun 2003 18:36:33 GMT, xx <x...@yyy.zzz> wrote:

>typedef unsigned char Boolean;
>
>class Inh1
>{
> public:
>
> virtual int foobar(void);
> virtual Boolean Test(void *t, Boolean b);
> virtual Boolean Test(int x, void *t, void *u);
>};
>
>class Inh2 : public Inh1
>{
> public:
> Boolean Test(void *t, unsigned char b);
>};
>
>int funct()
>{
> Inh2 i;
>
> i.foobar();
> i.Test(25, (void *)0, (void *)0);
> return 0;
>}
>
>In the above example, Inh1 declares two functions which are overloaded.

Yes

>Inh2 is a class which overides the implementation of one of the
>overloaded function, but when the other is to be used, the compiler does
>not see the overloaded function.

Yes, it overrides one and hides the other. If you are interested in
using the hidden one, you could use the fully qualified name
Inh1::Test.

>Obviously, foobar() gets carried into the definition of Inh2, why
>doesn't the overloaded version of Test?

The second overloaded version of Inh1::Test is hidden. It is still
inherited. A function member of a derived class is not in the same
scope as the function member of the same name in a base class. So it
is parallel to the general rule of hiding regular functions:

{
int f(const char*); // 1
int g()
{
extern f(int); // 2, different scope
f("a"); // error, #1 is hidden by #2.
// No f(const char*) in this scope.
}
}

I believe that the assumption about hidden names is that you are not
interested in some of the overloaded functions because you did not
provide overridding functions for them. So those functions are weeded
out of the set of potential overloaded function resolution set.

Best wishes,

Bob

Josh Sebastian

unread,
Jun 20, 2003, 5:26:47 PM6/20/03
to
In addition to Bob's excellent explanation, you can get the "expected"
behavior with a using declaration:

class Inh2 : public Inh1 {
public:

using Inh1::foo;

// rest as before
};

This will bring all the names of foo from Inh1 into Inh2's scope, so your
override won't hide them.

Josh

Chris Newton

unread,
Jun 21, 2003, 4:25:15 PM6/21/03
to
Robert W Hand wrote:
> I believe that the assumption about hidden names is that you are not
> interested in some of the overloaded functions because you did not
> provide overridding functions for them. So those functions are weeded
> out of the set of potential overloaded function resolution set.

As I understand it, there was actually a good reason for this decision
being made as it was, though personally I disagree with it. Suppose you
have the following:
struct Base
{
virtual void f(int);
};

struct Derived : Base
{
virtual void f(int);
};
I can use the function f as follows:
Derived d;
char x;
d.f(x);
This calls f(int), and in particular, it calls the version in Derived.
Now suppose I change the base class as follows:
struct Base
{
virtual void f(int);
virtual void f(char);
};
If the name-hiding rule weren't as it is, then now the calling code
(which used only the derived class, remember) would wind up calling the
newly added overload, f(char), in the Base class. That is, the behaviour
of the calling code has been silently changed by changing only the base
class. From what I've heard, it was felt that this was too dangerous,
and thus the name-hiding rule was given its current form.

As I noted earlier, I personally regard this decision as unfortunate. I
think far more people are surprised that their code doesn't compile
because of this rule, and the "using" hack is awkward and limited. In my
opinion, the "surprising" behaviour they were guarding against is what
logically should happen, given the "is-a" principle of inheritance
amongst other things. I'm not alone in this view, even amongst experts
on the more high powered C++ newsgroups, but as things stand today, the
name hiding rule is what it is for the reasons I've described above.

Cheers,
Chris

Robert W Hand

unread,
Jun 22, 2003, 8:08:34 AM6/22/03
to
On Sat, 21 Jun 2003 20:25:15 +0000 (UTC), Chris Newton
<chris...@no.junk.please.btinternet.com> wrote:

>Robert W Hand wrote:
>> I believe that the assumption about hidden names is that you are not
>> interested in some of the overloaded functions because you did not
>> provide overridding functions for them. So those functions are weeded
>> out of the set of potential overloaded function resolution set.
>
>As I understand it, there was actually a good reason for this decision
>being made as it was, though personally I disagree with it. Suppose you
>have the following:

<snip code>


>If the name-hiding rule weren't as it is, then now the calling code
>(which used only the derived class, remember) would wind up calling the
>newly added overload, f(char), in the Base class. That is, the behaviour
>of the calling code has been silently changed by changing only the base
>class. From what I've heard, it was felt that this was too dangerous,
>and thus the name-hiding rule was given its current form.

Your explanation is a good one. In addition, the problem of keeping
track of hidden overloads grows considerably if there are several
layers of inheritance scattered over a dozen files.

>
>As I noted earlier, I personally regard this decision as unfortunate. I
>think far more people are surprised that their code doesn't compile
>because of this rule, and the "using" hack is awkward and limited. In my
>opinion, the "surprising" behaviour they were guarding against is what
>logically should happen, given the "is-a" principle of inheritance
>amongst other things. I'm not alone in this view, even amongst experts
>on the more high powered C++ newsgroups, but as things stand today, the
>name hiding rule is what it is for the reasons I've described above.

I was surprised by this rule the first time that I came across it.
Some rules seem self-apparent. Think of the strangeness of a language
that defined the syntax for integer addition as:

a (( b ^^ c;

instead of as:

a = b + c;

All programmers would complain.

Other rules are more arbitrary. I think that the current rule under
discussion is such a rule. The best, that we can hope for, is that a
committee of experienced and smart programmers will devote adequate
time, discussion and reflection to make the "best" choice. Once that
choice is made, we programmers must readjust our sense of correctness
to that choice. For example, it took no time for either you or me to
see the error in the OP code. We have adjusted ourselves to the new
rule.

Best wishes,

Bob

0 new messages