Tuesday, October 25, 2011

Behind the Scenes: Name Hiding in C++

Name hiding happens when an overloaded member function is declared in the scope of a derived class. Here is an example:
class Base
{
public:
    virtual ~Base() {}

    virtual void someFunction() const;
    virtual void someFunction(int x) const;
};

void Base::someFunction() const {}
void Base::someFunction(int x) const {}

class Derived : public Base
{
public:
    void someFunction() const;
};

void Derived::someFunction() const {}

int main(int argc, char **argv)
{
    Derived d;
    d.someFunction(123);         

    return 0;
}
This, in fact, will not compile and gcc reports the following error:
error: no matching function for call to 'Derived::someFunction(int)'
While if we remove Derived::someFunction(), the code compiles without errors. First of all, note that there are two orthogonal language features involved here: function overloading and inheritance (overloading vs. overriding).


The scope of Derived becomes nested within the scope of its base class, however, introducing a name in the derived class obscures all overloaded versions of the same function in the Base class, hence the compiler error. This is actually standard behavior, for reasons which will be discussed below.

Overload resolution

Function overloading takes place when two, or more functions have
  • the same name, and
  • the same return type, but
  • different signatures.

But what exactly constitutes a signature? My copy of the C++ Standard draft document has the following to say about function signatures:
1.3.11 signature
the information about a function that participates in overload resolution (13.3): its parameter-type-list (8.3.5) and, if the function is a class member, the cv-qualifiers (if any) on the function itself and the class in which the member function is declared.2) The signature of a function template specialization includes the types of its template arguments (14.5.5.1).
Additionally, footnote 2 says:
Function signatures do not include return type, because that does not participate in overload resolution.
In other words, a function's signature is formed by
  • the number of parameters;
  • the data type of the parameters;
  • the order in which they appear; and
  • the function's cv-qualifiers, if any (in the case of class members)

The process of determining the actual call is referred to as overload resolution, and takes place at compile time. Here are three functions with the same name and return type, but different signatures:
void scoobydoo();
void scoobydoo(int x);
void scoobydoo(int x, int y);
Overload resolution works by choosing the best function from a number of candidates, matching the arguments to the parameters of the candidates.
int main()
{
    scoobydoo(2);      // matches void scoobydoo(int x);
    return 0;
}
When identifying possible candidates, name lookup propagates outward starting from the immediate scope. Only if no function exists with the correct name does the process continue to the next enclosing scope. Once a scope is found that has at least one viable candidate, the compiler proceeds with matching the arguments to the parameters of the various candidates, applying access rules etc.
class Derived : public Base
{
public:
    void someFunction() const;
};
Since someFunction is found in the scope of Derived, the compiler doesn't look any further. Instead, it continues to select the most appropriate function from the set of candidates, which in this case only consists of the version of someFunction with no parameters.

The fragile base class problem

Now, the actual reason behind this whole name hiding business is to address something called the fragile base class problem, which refers to a situation when changes made to a base class can potentially harm code that uses derived classes. Here is an example:
class A {};

class B : public A
{
public:
    void f(float x) { std::cout << "B::f"; }
};

int main()
{
    B b;
    b.f(5);
    return 0;
}
Suppose class A is part of a library, and B is client code. Make the following change in A,
class A
{
public:
    virtual void f(int x) { std::cout << "A::f"; }
};
and all of a sudden, the int is a better match to the argument supplied in main(). Without name hiding we would have changed the behavior of the program. Instead, because of this feature, A::f() is never considered by the name lookup mechanism – it is hidden.

Solutions/work-arounds

How can we make someFunction(int x) available also in the scope of our Derived class then? Essentially, there are three different ways of doing this.

1. Use fully qualified name
A qualified call bypasses the virtual dispatch mechanism:
int main(int argc, char **argv)
{
    Derived d;
    d.Base::someFunction(123);         

    return 0;
}
This, however, is usually not a very good design choice. Was Derived ever to change, and implement someFunction(int x), client code would need to change as well.

2. Reimplementing
Another solution is to reimplement someFunction(int x) in Derived:
class Derived : public Base
{
public:
    void someFunction() const;
    void someFunction(int x) const;
};

void Derived::someFunction() const {}
void Derived::someFunction(int x) const { Base::someFunction(x); }

3. The using directive
A using declaration also allows us to re-introduce someFunction in the scope of the derived class. This, in fact, is the recommended approach.
class Derived : public Base
{
public:
    using Base::someFunction;

    void someFunction() const;
};

void Derived::someFunction() const {}

int main(int argc, char **argv)
{
    Derived d;
    d.someFunction(123);         

    return 0;
}
 

6 comments:

Anonymous said...

Very well written post - thanks!

Another page on name hiding in C++ that helped me:

http://www.programmerinterview.com/index.php/c-cplusplus/c-name-hiding/

svrtechnologies said...

This post is really nice and informative. The explanation given is really comprehensive and informative.....

microsoft azure training

nandhini said...

This post is much helpful for us. This is really very massive value to all the readers and it will be the only reason for the post to get popular with great authority.
Microsoft Windows Azure Training | Online Course | Certification in chennai | Microsoft Windows Azure Training | Online Course | Certification in bangalore | Microsoft Windows Azure Training | Online Course | Certification in hyderabad | Microsoft Windows Azure Training | Online Course | Certification in pune

Steve Jhon said...

http://gbasibe.com
http://gbasibe.com
http://gbasibe.com
http://gbasibe.com
http://yaando.com/
[no anchor text]
[no anchor text]
click here to visit the site

Steve Jhon said...

[Click here to visit the site]
http://gbasibe.com
http://gbasibe.com
【回上頁】
【回上頁】
http://gbasibe.com
http://gbasibe.com
http://gbasibe.com

Anonymous said...

çekmeköy bosch klima servisi
ataşehir bosch klima servisi
tuzla mitsubishi klima servisi
ümraniye arçelik klima servisi
beykoz samsung klima servisi
üsküdar samsung klima servisi
beykoz mitsubishi klima servisi
üsküdar mitsubishi klima servisi
tuzla lg klima servisi

Post a Comment