What's the rationale behind the hiding rule in C++?
class A { void f(int); }
class B : public A { void f(double); } // B::f(int) is hidden
If it is a meaningful feature I think it should also be possible to hide functions without defining new functions with the same name: something like this:
class B : public A { hide void f(double); }
but this is not possible.
I don't think it simplifies compilers job, since compilers must anyway be able to unhide functions when you explicitly use the
using
directive:class B : public A { using A::f; void f(double); } // B::f(int) NOT hidden
So, how come there is a hiding rule?
Hum, all the three answers seem to be good, and show different rationales for the hiding rule. I'm not sure about which answer I should accept.
It's an hairy question, but apparently the idea is that this hiding feature helps avoiding subtle bugs when making changes to a base class (that could otherwise "steal" calls that before would have been handled by the derived class). Still a change in a base class can influence the result of compilation of derived classes so I don't think I understand 100% this explanation.
I agree that this topic is so frequently discussed that probably the hiding actually increases the amount of "surprises" in C++ programmers.
A detailed discussion about this issue can be found here...
i don't know the original rationale, but since hide or not hide are about equally bad choices wrt. to functions, i'm guessing the rationale is to have uniform rules: the same as for names defined in nested curly-braces scopes.
the hiding helps you in some ways.
adding a method to a base class will by default not affect overload resolution for a derived class.
and you do not run afoul of overload resolution by some mishap directing your call with say argument false
, to a base class method with formal argument void*
. such things.
cheers & hth.,
I'm sure I've seen this case offered by a C++ bigwig, not sure which:
struct Base {
void f(const Base&);
};
struct Derived : Base {
using Base::f;
void f(double);
};
int main() {
Derived d;
d.f('a'); // calls Derived::f
}
Now, add void f(int);
to Base
, and the meaning of main changes - it calls Base::f
because int
is a better match for char
- it's an integer promotion rather than a standard conversion.
It's not clear whether that change to the base would really be intended by the programmer to catch calls with char
, so requiring using
to be explicit means the default behavior is that the change doesn't affect the calling code. I believe it's a marginal call, but I think the committee decided that base classes in C++ were fragile enough as they are, without this too :-)
There's no need for a "hide" keyword because there's no comparable case for hiding "f" from the Base when it isn't overloaded in Derived.
Btw, I've chosen the types, and char
is deliberately incongruous. You can get more subtle cases with int
vs unsigned int
rather than int
vs char
.
Another reason for hiding base class's member function (with same name but different signatures) might be due to ambiguity caused by optional parameters. Consider the following example:
#include <stdio.h>
class A
{
public:
int foo(int a, int b=0)
{
printf("in A : %d, %d\n", a, b);
}
};
class B : public A
{
public:
int foo(int a)
{
printf("in B : %d\n", a);
foo(a); //B:foo(a) will be called unless we explicitly call A:foo(a)
foo(a, 1); // compile error: no matching function for call to B:foo(int&, int)
}
};
int main()
{
B b;
b.foo(10);
return 0;
}
If the foo
method in base class hadn't become hidden, it wouldn't be possible for compiler to decide whether A::foo
should be called or B::foo
since the following line matches both signatures:
foo(a);
Probably, the reason is template specialization. I give you an example:
template <int D> struct A { void f() };
template <> struct A<1> { void f(int) };
template <int D>
struct B: A<D>
{
void g() { this->f(); }
};
The template class B has a method f()
, but until you don't create an instance of the class B you don't know the signature. So the call this->f()
is anytime "legal". Both GCC and CLang don't report error until you create the instance. But when you call the method g()
on a B<1>
instance they indicate the error. So the hiding rule keep simpler to check if your code is valid.
I report the last part of code used in my example.
int main (int argc, char const *argv[])
{
B<0> b0; /* valid */
B<1> b1; /* valid */
b0.g(); /* valid */
b1.g(); /* error: no matching function for call to ‘B<1>::f()’ */
return 0;
}
来源:https://stackoverflow.com/questions/4837399/c-rationale-behind-hiding-rule