Should this really be a compile error?

R

rob.iverson

We ran into this problem and I can't see why the compilers (Sun One
Studio 8 and g++ 2.95) think it's an error:

-------------------

class A
{
public:
void foo();

};

class B : public A
{
public:
void foo(int i);

};

void B::foo(int i)
{
foo(); // Both compilers give an error here.

}

class C
{

public:
void foo();

void foo(int i);

};

void C::foo(int i)
{
foo(); // Neither compiler gives an error here.

}
 
R

Rolf Magnus

We ran into this problem and I can't see why the compilers (Sun One
Studio 8 and g++ 2.95) think it's an error:

Dunno about the Sun compiler, bug g++ 2.95 is very old. You should upgrade
it.
class A
{
public:
void foo();

};

class B : public A
{
public:
void foo(int i);

};

void B::foo(int i)
{
foo(); // Both compilers give an error here.

They are coorect.
}

class C
{

public:
void foo();

void foo(int i);

};

void C::foo(int i)
{
foo(); // Neither compiler gives an error here.

They are again correct.
}

---------------------

Clearly it has something to do with the inheritance, but there is a
valid foo() call in the scope of B's foo(int). If we call "A::foo()"
instead, it works without complaint.

That's how it's supposed to be. A function in a derived class hides all
functions of the base class that have the same name. You must either
qualify it with A::foo() or add:

using A::foo;

to B's class definition.
 
D

Daniel T.

We ran into this problem and I can't see why the compilers (Sun One
Studio 8 and g++ 2.95) think it's an error:

-------------------

class A
{
public:
void foo();

};

class B : public A
{
public:
void foo(int i);

};

void B::foo(int i)
{
foo(); // Both compilers give an error here.

}

'foo()' is hidden in this scope, thus the error.

class B : public A {
using foo;
public:
void foo(int i);
};

Will stop the error.
 
S

Stuart Golodetz

Rolf Magnus said:
Dunno about the Sun compiler, bug g++ 2.95 is very old. You should upgrade
it.


They are coorect.


They are again correct.


That's how it's supposed to be. A function in a derived class hides all
functions of the base class that have the same name. You must either
qualify it with A::foo() or add:

using A::foo;

to B's class definition.

Why is it supposed to be like that, out of interest? What's the problem
with/disadvantage of not hiding base functions with the same name?

Cheers,
Stu
 
J

Jerry Coffin

We ran into this problem and I can't see why the compilers (Sun One
Studio 8 and g++ 2.95) think it's an error:

They're right.
class A
{
public:
void foo();

};

class B : public A
{
public:
void foo(int i);

};

void B::foo(int i)
{
foo(); // Both compilers give an error here.

B::foo() hides A::foo() at this point. The compiler basically
searches back through the inheritance tree only as far as necessary
to find at least one function named foo. It then attempts to select
among any overloads of the name in that scope. If it can't find a
suitable overload it does NOT search further back through the
inheritance tree for more overloads -- it stops and gives an error.

As you've already noted, explicitly qualifying your call to A::foo()
allows the compiler to find the correct function. You can also use a
using declaration to get the compiler to treat A::foo() as if it was
in the same scope as B::foo():

class B : public A {
using A::foo;
public:
void foo(int i) {
foo();
}
};

Note, however, that this applies to A::foo in general -- if A
contains several overloads of foo(), it makes _all_ of them appear
local. It doesn't bring just one of those overloads into scope.

To summarize: functions with the same name at different scopes do NOT
overload each other. Rather, the name(s) at the inner scope hide the
name(s) at the outer scope, unless you take steps to prevent them
from begin hidden. As you'd expect, code like yours in which a
function in a derived class hides a function from a base class is
generally a poor idea. It can easily (and will often) lead to
unexpected results.
 
V

Victor Bazarov

Stuart said:
[..] What's the
problem with/disadvantage of not hiding base functions with the same
name?

Name hiding transcends member functions. It's more general and making
it different for classes would overcomplicate things. Have you tried
looking in D&E? I just searched in Google Groups for <"name hiding"
why class> and got enough discussions to keep one busy for a day (the
double quotes are needed, the 'class' word is supposed to keep non-C++
results out). Please don't ask us to start another beating of this
dead horse.

V
 
S

Stuart Golodetz

Victor Bazarov said:
Stuart said:
[..] What's the
problem with/disadvantage of not hiding base functions with the same
name?

Name hiding transcends member functions. It's more general and making
it different for classes would overcomplicate things. Have you tried
looking in D&E? I just searched in Google Groups for <"name hiding"
why class> and got enough discussions to keep one busy for a day (the
double quotes are needed, the 'class' word is supposed to keep non-C++
results out). Please don't ask us to start another beating of this
dead horse.

V

Ok, I'll check it out. I didn't realise it had been discussed before (I
probably should have looked first, thanks for pointing it out). "By all
means leave the poor beast alone", say I :)

Ta,
Stu
 
R

Rob

Jerry said:
They're right.


B::foo() hides A::foo() at this point. The compiler basically
searches back through the inheritance tree only as far as necessary
to find at least one function named foo. It then attempts to select
among any overloads of the name in that scope. If it can't find a
suitable overload it does NOT search further back through the
inheritance tree for more overloads -- it stops and gives an error.

As you've already noted, explicitly qualifying your call to A::foo()
allows the compiler to find the correct function. You can also use a
using declaration to get the compiler to treat A::foo() as if it was
in the same scope as B::foo():

class B : public A {
using A::foo;
public:
void foo(int i) {
foo();
}
};

Note, however, that this applies to A::foo in general -- if A
contains several overloads of foo(), it makes _all_ of them appear
local. It doesn't bring just one of those overloads into scope.

To summarize: functions with the same name at different scopes do NOT
overload each other. Rather, the name(s) at the inner scope hide the
name(s) at the outer scope, unless you take steps to prevent them
from begin hidden. As you'd expect, code like yours in which a
function in a derived class hides a function from a base class is
generally a poor idea. It can easily (and will often) lead to
unexpected results.

--
Later,
Jerry.

The universe is a figment of its own imagination.

Thanks for the detail (thanks to everyone else who's replied, too!).

I understand why this is happening, now. I guess I was mostly
surprised because the size of the argument lists made the call to the
base class not ambiguous at all. But thinking about it, a standard
that said "names from a base class are usually hidden, unless they are
member functions and the access of them is not ambiguous" would be
pretty silly...

I looked in my "Annotated C++ Reference Manual", and there is a little
example, mostly related to unexpected argument conversion and overload
selection. The authors pointed out that if overload selection included
methods of base classes (even far up the chain), results could be very
unexpected.

Anyway, thanks again!

Rob
 
J

Jerry Coffin

[ ... ]
Why is it supposed to be like that, out of interest? What's the problem
with/disadvantage of not hiding base functions with the same name?

First, it's inconsistent with the rest of the language. For example:

int my_f() {
float a;
{
int a;

a = 0.0;
}
}

Here, the assignment is clearly to the int, even though the float is
a better fit with the type of '0.0'.

Let's assume we changed the rules, and considered this an assignment
to the float. The second major problem can be seen by adding 'double
a' (or 'extern double a') outside the function. Now, even though the
function itself hasn't changed at all, the meaning of the assignment
has changed completely -- it's an assignment to a different variable
entirely.

This also interacts with another rule in C++: first it resolves
names, then it checks access. That means (for example) if you add a
variable in a base class, it participates in overload resolution the
same whether it's private, public or protected. IOW, adding almost
any name in the base class would potentially affect the results in
all derived classes, even if the base class name as private, so the
derived classes couldn't access it.

Such a change would make it nearly impossible to maintain the base
class. Nearly any change in the base class would require intimate
knowledge of all derived classes. A seemingly minor change in the
base class might completely change the meaning of code in some
derived classes, while rendering others completely illegal.
 
S

Stuart Golodetz

Jerry Coffin said:
[ ... ]
Why is it supposed to be like that, out of interest? What's the problem
with/disadvantage of not hiding base functions with the same name?

First, it's inconsistent with the rest of the language. For example:

int my_f() {
float a;
{
int a;

a = 0.0;
}
}

Here, the assignment is clearly to the int, even though the float is
a better fit with the type of '0.0'.

Let's assume we changed the rules, and considered this an assignment
to the float. The second major problem can be seen by adding 'double
a' (or 'extern double a') outside the function. Now, even though the
function itself hasn't changed at all, the meaning of the assignment
has changed completely -- it's an assignment to a different variable
entirely.

This also interacts with another rule in C++: first it resolves
names, then it checks access. That means (for example) if you add a
variable in a base class, it participates in overload resolution the
same whether it's private, public or protected. IOW, adding almost
any name in the base class would potentially affect the results in
all derived classes, even if the base class name as private, so the
derived classes couldn't access it.

Such a change would make it nearly impossible to maintain the base
class. Nearly any change in the base class would require intimate
knowledge of all derived classes. A seemingly minor change in the
base class might completely change the meaning of code in some
derived classes, while rendering others completely illegal.

--
Later,
Jerry.

The universe is a figment of its own imagination.

Thanks, that makes sense now you put it like that :)

Cheers,
Stu
 
M

Marcus Kwok

We ran into this problem and I can't see why the compilers (Sun One
Studio 8 and g++ 2.95) think it's an error:

-------------------

class A
{
public:
void foo();

};

class B : public A
{
public:
void foo(int i);

};

void B::foo(int i)
{
foo(); // Both compilers give an error here.

}

class C
{

public:
void foo();

void foo(int i);

};

void C::foo(int i)
{
foo(); // Neither compiler gives an error here.

}

---------------------

Clearly it has something to do with the inheritance, but there is a
valid foo() call in the scope of B's foo(int). If we call "A::foo()"
instead, it works without complaint.

Others have answered this very well, but this is also in the FAQ:
http://www.parashift.com/c++-faq-lite/strange-inheritance.html#faq-23.9
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,780
Messages
2,569,611
Members
45,281
Latest member
Pedroaciny

Latest Threads

Top