Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Reason for C++ member function hiding [duplicate]

Tags:

c++

Possible Duplicate:
name hiding and fragile base problem

I'm familiar with the rules involving member function hiding. Basically, a derived class with a function that has the same name as a base class function doesn't actually overload the base class function - it completely hides it.

struct Base
{
    void foo(int x) const
    {

    }
};

struct Derived : public Base
{
    void foo(const std::string& s) { }
};


int main()
{
    Derived d;
    d.foo("abc");
    d.foo(123); // Will not compile! Base::foo is hidden!
}

So, you can get around this with a using declaration. But my question is, what is the reason for base class function hiding? Is this a "feature" or just a "mistake" by the standards committee? Is there some technical reason why the compiler can't look in the Base class for matching overloads when it doesn't find a match for d.foo(123)?

like image 778
Channel72 Avatar asked Aug 12 '12 16:08

Channel72


People also ask

What is function hiding CPP?

This process is referred to as name hiding. In a member function definition, the declaration of a local name hides the declaration of a member of the class with the same name. The declaration of a member in a derived class hides the declaration of a member of a base class of the same name.

What will happen if we call a derived class function overridden through a base class object?

Suppose, the same function is defined in both the derived class and the based class. Now if we call this function using the object of the derived class, the function of the derived class is executed. This is known as function overriding in C++. The function in derived class overrides the function in base class.

Can a derived class make a public base function private?

You can only change the access specifiers of base members the derived class would normally be able to access. Therefore, you can never change the access specifier of a base member from private to protected or public, because derived classes do not have access to private members of the base class.

Can base class call derived class function?

Even though the derived class can't call it in the base class, the base class can call it which effectively calls down to the (appropriate) derived class. And that's what the Template Method pattern is all about.


3 Answers

Name lookup works by looking in the current scope for matching names, if nothing is found then it looks in the enclosing scope, if nothing is found it looks in the enclosing scope, etc. until reaching the global namespace.

This isn't specific to classes, you get exactly the same name hiding here:

#include <iostream>

namespace outer
{
  void foo(char c) { std::cout << "outer\n"; }

  namespace inner
  {
    void foo(int i) { std::cout << "inner\n"; }

    void bar() { foo('c'); }
  }
}

int main()
{
  outer::inner::bar();
}

Although outer::foo(char) is a better match for the call foo('c') name lookup stops after finding outer::inner::foo(int) (i.e. outer::foo(char) is hidden) and so the program prints inner.

If member function name weren't hidden that would mean name lookup in class scope behaved differently to non-class scope, which would be inconsistent and confusing, and make C++ even harder to learn.

So there's no technical reason the name lookup rules couldn't be changed, but they'd have to be changed for member functions and other types of name lookup, it would make compilers slower because they'd have to continue searching for names even after finding matching names in the current scope. Sensibly, if there's a name in the current scope it's probably the one you wanted. A call in a scope A probably wants to find names in that scope, e.g. if two functions are in the same namespace they're probably related (part of the same module or library) and so if one uses the name of the other it probably means to call the one in the same scope. If that's not what you want then use explicit qualification or a using declaration to tell the compiler the other name should be visible in that scope.

like image 175
Jonathan Wakely Avatar answered Oct 09 '22 20:10

Jonathan Wakely


I believe @Lol4t0 is pretty much correct, but I'd state things much more strongly. If you allowed this, you'd end up with two possibilities: either make a lot of other changes throughout almost the entirety of the language, or else you end up with something almost completely broken.

The other changes you'd make to allow this to work would be to completely revamp how overloading is done -- you'd have to change at least the order of the steps that were taken, and probably the details of the steps themselves. Right now, the compiler looks up the name, then forms an overload set, resolves the overload, then checks access to the chosen overload.

To make this work even sort of well, you'd pretty much have to change that to check access first, and only add accessible functions to the overload set. With that, at least the example in @Lol4t0's answer could continue to compile, because Base::foo would never be added to the overload set.

That still means, however, that adding to the interface of the base class could cause serious problems. If Base didn't originally contain foo, and a public foo were added, then the call in main to d.foo() would suddenly do something entirely different, and (again) it would be entirely outside the control of whoever wrote Derived.

To cure that, you'd just about have to make a fairly fundamental change in the rules: prohibit implicit conversions of function arguments. Along with that, you'd change overload resolution so in case of a tie, the most derived/most local version of a function was favored over a less derived/outer scope. With those rules, the call to d.foo(5.0) could never resolve to Derived::foo(int) in the first place.

That, however, would only leave two possibilities: either calls to free functions would have different rules than calls to member functions (implicit conversions allowed only for free functions) or else all compatibility with C would be discarded entirely (i.e., also prohibit implicit conversions in all function arguments, which would break huge amounts of existing code).

To summarize: to change this without breaking the language entirely, you'd have to make quite a few other changes as well. It would almost certainly be possible to create a language that worked that way, but by the time you were done it wouldn't be C++ with one minor change -- it would be an entirely different language that wasn't much like C++ or C, or much of anything else.

like image 5
Jerry Coffin Avatar answered Oct 09 '22 21:10

Jerry Coffin


Is this a "feature" or just a "mistake" by the standards committee?

It's definitely not a mistake, since it's clearly stipulated in the standard. It's a feature.

Is there some technical reason why the compiler can't look in the Base class for matching overloads when it doesn't find a match for d.foo(123)?

Technically, a compiler could look in the base class. Technically. But if it did, it would break the rules set by the standard.

But my question is, what is the reason for base class function hiding?

Unless someone from the committee comes with an answer, I think we can only speculate. Basically, there were two options:

  • if I declare a function with the same name in a derived class, keep the base class's functions with the same name directly accessible through a derived class
  • don't

It could have been determined by flipping a coin (...ok, maybe not).

In general, what are the reasons for wanting a function with the same name as that of a base class? There's different functionality - where you'd more likely use polymorphism instead. For handling different cases (different parameters), and if these cases aren't present in the base class, a strategy pattern might be more appropriate to handle the job. So most likely function hiding comes in effect when you actually do want to hide the function. You're not happy with the base class implementation so you provide your own, with the option of using using, but only when you want to.

I think it's just a mechanism to make you think twice before having a function with the same name & different signature.

like image 10
Luchian Grigore Avatar answered Oct 09 '22 22:10

Luchian Grigore