C++: rationale behind hiding rule
Asked Answered
S

5

28

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?

Sapphira answered 29/1, 2011 at 14:28 Comment(3)
While the decision was not based on easing the life of compiler writers, the argument that the compiler still needs to unhide is invalid. When the compiler parses the class with the using, it brings all the A::f into the in memory representation of the class. When it tries to resolve a call, it only needs to go back as far as needed until it finds the first occurrence of the identifier. There is no need to keep going back accross possibly multiple paths to bring all possible identifiers into scope. The same goes for the fact that a member method will hide a namespace level function...Coady
AFAIK, hiding without defining new functions is possbile in C++11 via =delete.Vollmer
There is a mechanism for hiding base class functions. Use class A : protected B { ... }; instead of public. Sorry for beating a dead horse.Coarctate
C
12

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...

Corody answered 29/1, 2011 at 14:55 Comment(1)
Yes, I have just spent 2 hours surprised by why won't the compiler see my function when the example on the internet works fine.Sard
E
10

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.,

Extended answered 29/1, 2011 at 15:3 Comment(1)
I'm baffled you're actually the only one who talked about consistency with hiding functions from others scopes! I still think it is mostly a matter of avoiding surprises.Appledorf
Y
8

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.

Yezd answered 29/1, 2011 at 16:44 Comment(0)
C
3

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);
Clerihew answered 16/1, 2012 at 12:28 Comment(0)
A
-2

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;
}
Arduous answered 31/1, 2011 at 16:44 Comment(1)
Except that, in your example, there is absolutely nothing in template class B that would hide the method f that is declared in template class A... The error does not come from the fact that f(void) is hidden in B<1> (why would it be more hidden in B<1> than in B<0> since there is no specialization). Your problem is just that f(void) is not declared at all in A<1> : there is nothing to hide.Elconin

© 2022 - 2024 — McMap. All rights reserved.