C++ overload resolution [duplicate]
Asked Answered
K

9

42

Given the following example, why do I have to explicitly use the statement b->A::DoSomething() rather than just b->DoSomething()?

Shouldn't the compiler's overload resolution figure out which method I'm talking about?

I'm using Microsoft VS 2005. (Note: using virtual doesn't help in this case.)

class A
{
  public:
    int DoSomething() {return 0;};
};

class B : public A
{
  public:
    int DoSomething(int x) {return 1;};
};

int main()
{
  B* b = new B();
  b->A::DoSomething();    //Why this?
  //b->DoSomething();    //Why not this? (Gives compiler error.)
  delete b;
  return 0;
}
Knavish answered 16/9, 2008 at 13:9 Comment(1)
I am trying to call DoSomething() in class A using a pointer to class B.Knavish
H
44

The two “overloads” aren't in the same scope. By default, the compiler only considers the smallest possible name scope until it finds a name match. Argument matching is done afterwards. In your case this means that the compiler sees B::DoSomething. It then tries to match the argument list, which fails.

One solution would be to pull down the overload from A into B's scope:

class B : public A {
public:
    using A::DoSomething;
    // …
}
Heterotaxis answered 16/9, 2008 at 13:18 Comment(2)
In fact if they are not in the same scope, they are not called overloads as wellCarner
@Chubsdad: That’s why I put the word in quotes. And once the method is pulled into the same scope, it becomes an overload.Heterotaxis
R
14

Overload resolution is one of the ugliest parts of C++

Basically the compiler finds a name match "DoSomething(int)" in the scope of B, sees the parameters don't match, and stops with an error.

It can be overcome by using the A::DoSomething in class B

class A  
{  
  public:  
    int DoSomething() {return 0;}
};  

class B : public A  
{  
  public:  
    using A::DoSomething;
    int DoSomething(int x) {return 1;} 
};   


int main(int argc, char** argv)
{
  B* b = new B();  
  // b->A::DoSomething();    // still works, but...
  b->DoSomething();    // works now too
  delete b;  
  return 0;
}
Recuperator answered 16/9, 2008 at 13:18 Comment(0)
R
5

No, this behaviour is present to ensure that you don't get caught out inheriting from distant base classes by mistake.

To get around it, you need to tell the compiler which method you want to call by placing a using A::DoSomething in the B class.

See this article for a quick and easy overview of this behaviour.

Realistic answered 16/9, 2008 at 13:27 Comment(0)
O
5

The presence of a method in a derived class hides all methods with the same name (regardless of parameters) in base classes. This is done to avoid problems like this:

class A {} ;
class B :public A
{
    void DoSomething(long) {...}
}

B b;
b.DoSomething(1);     // calls B::DoSomething((long)1));

than later someone changes class A:

class A
{
    void DoSomething(int ) {...}
}

now suddenly:

B b;
b.DoSomething(1);     // calls A::DoSomething(1);

In other words, if it didn't work like this, a unrelated change in a class you don't control (A), could silently affect how your code works.

Orange answered 16/9, 2008 at 17:21 Comment(1)
Very nice point, I must say. Most answers I have seen talked about why error occurs and how to avoid it but your point shows why this feature is essential and useful in real world scenario!!Nobelium
G
3

This has something to do with the way name resolution works. Basically, we first find the scope from which the name comes, and then we collect all overloads for that name in that scope. However, the scope in your case is class B, and in class B, B::DoSomething hides A::DOSomething:

3.3.7 Name hiding [basic.scope.hiding]

...[snip]...

3 In a member function definition, the declaration of a local name hides the declaration of a member of the class with the same name; see basic.scope.class. The declaration of a member in a derived class (class.derived) hides the declaration of a member of a base class of the same name; see class.member.lookup.

Because of name hiding, A::DoSomething is not even considered for overload resolution

Gaberones answered 16/9, 2008 at 13:30 Comment(1)
Having the actual specification reference is good.Moravian
C
2

When you define a function in a derived class then it hides all the functions with that name in the base class. If the base class function is virtual and has a compatible signature then the derived class function also overrides the base class function. However, that doesn't affect the visibility.

You can make the base class function visible with a using declaration:

class B : public A  
{  
  public:  
    int DoSomething(int x) {return 1;};  
    using A::DoSomething;
};  
Carolecarolee answered 16/9, 2008 at 13:19 Comment(0)
P
2

That's not overloading! That's HIDING!

Pantograph answered 16/9, 2008 at 13:57 Comment(0)
G
1

When searching up the inheritance tree for the function to use, C++ uses the name without arguments, once it has found any definition it stops, then examines the arguments. In the example given, it stops in class B. In order to be able to do what you are after, class B should be defined like this:

class B : public A  
{  
  public:
    using A::DoSomething;
    int DoSomething(int x) {return 1;};  
}; 
Grose answered 16/9, 2008 at 13:19 Comment(0)
V
1

The function is hidden by the function with the same name in the subclass (but with a different signature). You can unhide it by using the using statement, as in using A::DoSomething();

Vierra answered 16/9, 2008 at 13:20 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.