C++: rationale behind hiding rule

2019-01-04 12:55发布

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.

5条回答
淡お忘
2楼-- · 2019-01-04 13:23

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;
}
查看更多
霸刀☆藐视天下
3楼-- · 2019-01-04 13:25

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);
查看更多
闹够了就滚
4楼-- · 2019-01-04 13:32

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

查看更多
5楼-- · 2019-01-04 13:42

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

查看更多
Fickle 薄情
6楼-- · 2019-01-04 13:49

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.

查看更多
登录 后发表回答