In this thread the author of the accepted answer explains why the overridden method in the derived class can not be resolved by the compiler. However the example is relative to a type cast resolution, that is both the base and derived overloaded method have one parameter only and the ambiguity is limited to that parameter type.
But where is the ambiguity when the overloaded methods have a different number of parameters, like in this example?
Note that I'm not asking why the example produces a compile error, I'm asking why the language was designed this way.
#include <iostream>
using namespace std;
class A
{
public:
inline int f(int x) { return x; }
};
class B: public A
{
public:
inline int f(int x, int y) { return x+y; }
};
int main()
{
B b;
cout << b.f(1) << endl; // COMPILE ERROR
cout << b.f(1,2) << endl;
}
The reason you get a compiler error is that f from class A is hidden by the f in class B.
When the compiler does member name lookup, it uses base classes only if the name is not found in the object's class, so it doesn't matter if you have a member in a base class that has a proper parameter list for your call.
From the standard:
10.2.5 Otherwise (i.e., C does not contain a declaration of f or the resulting declaration set is empty), S(f, C) is
initially empty. If C has base classes, calculate the lookup set for f in each direct base class subobject Bi
,
and merge each such lookup set S(f, Bi) in turn into S(f, C).
In C++, name lookup will stop looking for other names as soon as it find the requested name in one of the base classes.
In your case, the name f is defined in B, so the compiler stop looking in the other base classes.
You can make A::f visible with a using declaration :
class B: public A
{
public:
using A::f;
int f(int x, int y) { return x+y; }
};
The compiler will look for the implementation of function f in class B. The compiler found such an implementation, which has two arguments. You provided only one argument, so there is your error.
How would you take away overloads that make no sense in a derived class? Even in your example, assume that if you have a B instance, you wanted to forbid the use of the single-parameter function. As it is written now, you've removed the single-parameter version (well, at least removed it from name resolution in the context of a B instance). But if you wanted to still have that version available, you can specify using A::F; in your class to bring in the single-parameter version.
Related
Consider a class D inherited from two classes B and C, each of which inherits not-virtually class A. There is a method f in A, and the same named method in B hiding the method from A. I would like to call A::f() from B-base class of D object as follows:
struct A { void f() {} };
struct B : A { void f() {} };
struct C : A {};
struct D : B, C {};
int main() { D{}.B::A::f(); }
Unfortunately, it works only in MSVC, while both GCC and Clang produce the error:
error: 'A' is an ambiguous base of 'D'
demo: https://gcc.godbolt.org/z/jY3v876hK
It looks like GCC/Clang accept but completely ignore B:: prefix in B::A::f(). Are they right in doing so according to the standard?
Are they right in doing so according to the standard?
Yes. The nested name specifier in A::, B::A, or D::B::A all serve the same purpose, to name the class A.
[basic.lookup.classref]
4 If the id-expression in a class member access is a qualified-id of the form
class-name-or-namespace-name::...
the class-name-or-namespace-name following the . or -> operator is first looked up in the class of the object expression ([class.member.lookup]) and the name, if found, is used. Otherwise it is looked up in the context of the entire postfix-expression.
The nested name specifier does not name "a path" to the member, it names a base of D. And to name A results in ambiguity.
Other answers show why the compiler is correct.
In order to achieve what you want, you can do:
int main() { static_cast<B&&>(D{}).A::f(); }
It's a bit ugly, though.
https://gcc.godbolt.org/z/Mzs8rsdf5
Consider the code :
#include <stdio.h>
class Base {
public:
virtual void gogo(int a){
printf(" Base :: gogo (int) \n");
};
virtual void gogo(int* a){
printf(" Base :: gogo (int*) \n");
};
};
class Derived : public Base{
public:
virtual void gogo(int* a){
printf(" Derived :: gogo (int*) \n");
};
};
int main(){
Derived obj;
obj.gogo(7);
}
Got this error :
>g++ -pedantic -Os test.cpp -o test
test.cpp: In function `int main()':
test.cpp:31: error: no matching function for call to `Derived::gogo(int)'
test.cpp:21: note: candidates are: virtual void Derived::gogo(int*)
test.cpp:33:2: warning: no newline at end of file
>Exit code: 1
Here, the Derived class's function is eclipsing all functions of same name (not signature) in the base class. Somehow, this behaviour of C++ does not look OK. Not polymorphic.
Judging by the wording of your question (you used the word "hide"), you already know what is going on here. The phenomenon is called "name hiding". For some reason, every time someone asks a question about why name hiding happens, people who respond either say that this called "name hiding" and explain how it works (which you probably already know), or explain how to override it (which you never asked about), but nobody seems to care to address the actual "why" question.
The decision, the rationale behind the name hiding, i.e. why it actually was designed into C++, is to avoid certain counter-intuitive, unforeseen and potentially dangerous behavior that might take place if the inherited set of overloaded functions were allowed to mix with the current set of overloads in the given class. You probably know that in C++ overload resolution works by choosing the best function from the set of candidates. This is done by matching the types of arguments to the types of parameters. The matching rules could be complicated at times, and often lead to results that might be perceived as illogical by an unprepared user. Adding new functions to a set of previously existing ones might result in a rather drastic shift in overload resolution results.
For example, let's say the base class B has a member function foo that takes a parameter of type void *, and all calls to foo(NULL) are resolved to B::foo(void *). Let's say there's no name hiding and this B::foo(void *) is visible in many different classes descending from B. However, let's say in some [indirect, remote] descendant D of class B a function foo(int) is defined. Now, without name hiding D has both foo(void *) and foo(int) visible and participating in overload resolution. Which function will the calls to foo(NULL) resolve to, if made through an object of type D? They will resolve to D::foo(int), since int is a better match for integral zero (i.e. NULL) than any pointer type. So, throughout the hierarchy calls to foo(NULL) resolve to one function, while in D (and under) they suddenly resolve to another.
Another example is given in The Design and Evolution of C++, page 77:
class Base {
int x;
public:
virtual void copy(Base* p) { x = p-> x; }
};
class Derived : public Base{
int xx;
public:
virtual void copy(Derived* p) { xx = p->xx; Base::copy(p); }
};
void f(Base a, Derived b)
{
a.copy(&b); // ok: copy Base part of b
b.copy(&a); // error: copy(Base*) is hidden by copy(Derived*)
}
Without this rule, b's state would be partially updated, leading to slicing.
This behavior was deemed undesirable when the language was designed. As a better approach, it was decided to follow the "name hiding" specification, meaning that each class starts with a "clean sheet" with respect to each method name it declares. In order to override this behavior, an explicit action is required from the user: originally a redeclaration of inherited method(s) (currently deprecated), now an explicit use of using-declaration.
As you correctly observed in your original post (I'm referring to the "Not polymorphic" remark), this behavior might be seen as a violation of IS-A relationship between the classes. This is true, but apparently back then it was decided that in the end name hiding would prove to be a lesser evil.
The name resolution rules say that name lookup stops in the first scope in which a matching name is found. At that point, the overload resolution rules kick in to find the best match of available functions.
In this case, gogo(int*) is found (alone) in the Derived class scope, and as there's no standard conversion from int to int*, the lookup fails.
The solution is to bring the Base declarations in via a using declaration in the Derived class:
using Base::gogo;
...would allow the name lookup rules to find all candidates and thus the overload resolution would proceed as you expected.
This is "By Design". In C++ overload resolution for this type of method works like the following.
Starting at the type of the reference and then going to the base type, find the first type which has a method named "gogo"
Considering only methods named "gogo" on that type find a matching overload
Since Derived does not have a matching function named "gogo", overload resolution fails.
Name hiding makes sense because it prevents ambiguities in name resolution.
Consider this code:
class Base
{
public:
void func (float x) { ... }
}
class Derived: public Base
{
public:
void func (double x) { ... }
}
Derived dobj;
If Base::func(float) was not hidden by Derived::func(double) in Derived, we would call the base class function when calling dobj.func(0.f), even though a float can be promoted to a double.
Reference: http://bastian.rieck.ru/blog/posts/2016/name_hiding_cxx/
When I am learning function overloading, I know that a function differs from another by its name and number of parameters. So I try similar things for inheritance.
I declare a function without parameters in base class, and declare another function with the same name but different number of parameters in derived class.
From my function overloading and inheritance knowledge, the main code should work, but it turns out that the function in the base class is not inherited and I have to explicitly call it.
Could anyone explain to me about this behavior? Shouldn't breathe() be inherited by the derived class? Thanks a lot.
#include <iostream>
using namespace std;
//base class
class animal {
public:
void breathe(){
cout << "animal breathe" << endl;
}
};
//derived class
class fish: public animal {
public:
void breathe(int a){
cout << "fish bubble" << endl;
}
};
int main() {
fish fh;
// correct
fh.breathe(1);
// correct
fh.animal::breathe();
// error
fh.breathe();
return 0;
}
Google method hiding. Add
using animal::breathe;
To class fish
fh.breathe();
is an error message, because there is another breathe() defined in the derived class, which effectively hides the breathe() defined in the base class.
You can correct this by adding the statement:
using animal::breathe;
in the class fish.
Modified fish class:
class fish: public animal {
public:
//Make sure to make this statement public
//otherwise animal::breathe will not be accessible
//to the object of fish.
using animal::breathe;
void breathe(int a){
cout << "fish bubble" << endl;
}
};
When you declare a function in derived class with same name as in base class, you are hiding the base class function. To bring base class function within derived class scope, you have to explicitly specify it using using animal::breath like so:
class fish: public animal {
public:
using animal::breath;
void breathe(int a){
cout << "fish bubble" << endl;
}
};
n3376 10.2/3-4-5
The lookup set for f in C, called S(f, C), consists of two component
sets: the declaration set, a set of members named f; and the subobject
set, a set of subobjects where declarations of these members (possibly
including using-declarations) were found. In the declaration set,
using-declarations are replaced by the members they designate, and
type declarations (including injected-class-names) are replaced by the
types they designate. S(f, C) is calculated as follows:
> If C contains a declaration of the name f, the declaration set
contains every declaration of f declared in C that satisfies the
requirements of the language construct in which the lookup occurs. If
the resulting declaration set is not empty, the subobject set contains
C itself, and calculation is complete.
> Otherwise (i.e., C does not contain a declaration of f or the
resulting declaration set is empty), S(f, C) is initially empty. If C
has base classes, calculate the lookup set for f in each direct base
class subobject B i , and merge each such lookup set S(f, B i ) in
turn into S(f, C).
In your case, there is function breathe in class fish, compiler will not trying to found breathe in base class. You can resolve this by explicitly inserting using declaration in derived class.
using animal::breathe;
Now, compiler has two variant and due overloading resolution animal::breathe will be called.
Please look at the following code:
#include <iostream>
using namespace std;
class A {
public:
A() {};
virtual void foo(double d) { cout << d << endl; }
virtual void foo(double d, int a) = 0;
};
class B : public A {
public:
B() {};
virtual void foo(double d, int a) { cout << d << endl << a << endl; }
};
int main()
{
B b;
b.foo(3.14);
return 0;
}
The compiler (tried g++ and visual c++ 2008) says that there's no function like B:foo(double). The exact message of g++ is:
main.cpp:21: error: no matching function for call to ‘B::foo(double)’
It looks like the effect of hiding rule, but in my opinion the rule should not be used here, since I'm not overriding foo(double) and both foo methods are defined in base class.
I know that I can fix the problem with
using A::foo;
declaration in the derived class B.
Can you explain why the code does not compile and what rules of C++ apply here?
The hiding rule is not about overriding, it is about hiding of names. If the derived class declares a member function, this hides other base class member functions with the same name. This also happens in your case.
Names shadow, not specific functions. Once you make a foo in B, all base foo's (note, by name!) are shadowed.
When the compiler encounters an identifier, the lookup rules kick in and start searching for that identifier. In your concrete situation, with b.foo, the compiler knows that foo must be a member of B or one of its subclasses. The lookup rules state that the compiler must start with the most derived class (considering the static type of the object) and follow up in the hierarchy, and that once the identifier is found in one level only definitions in that level will be considered, it must not keep looking upwards.
B& f(); // might return a B or something derived from B
void test() {
B& b = f(); // static type is B
b.foo(1.0);
}
Regardless of what f returns, the static type is B, so the compiler will lookup in B class and find B::foo(double,int). Since there is no other foo declaration at that level, the compiler must try to match (and fail) the function call with the available method declarations.
The important thing is that the lookup does not look the object but rather looks by type and going upwards, cutting as soon as it encounters the first instance.
Looks perfectly reasonable to me. Although function signature does matter to know what the function is, I can see how this behavior prevents very stupid mistakes.
As ereOn suggested, using directive would be a fair price to pay.
Suppose I have this:
class A
{
public:
virtual int hello(A a);
};
class B : public A
{
public:
int hello(B b){ bla bla };
};
So, A it's an abstract class.
1)In the class B, I'm defining a method that its suppose overrides the A class. But the parameter it's slightly different. I'm not sure about this, is this correct? Maybe because of polymorphism, this is ok but its rather confusing.
2) If I do: A a = new B;, and then a.hello(lol); if "lol" it's not of type B, then it would give compile error?, and if it's of type A from another class C (class C : public A), what would happend?
I'm confused about the overriding and virtual thing.. all examples I found work with methods without parameters.
Any answer, link, or whatever it's appreciated.
thanks
pd: sorry for my english
Your class B doesn't override the member function in A, it overloads it. Or tries to anyway, see the bit about hiding later.
Overriding is when a derived class defines its own version of a virtual member function from a base class. Overloading is when you define different functions with the same name.
When a virtual call is made on a pointer or reference that has the type of the base class, it will only "consider" overrides in the derived class, not overloads. This is essential - for an instance of B to be treated by callers as though it does everything an A can do (which is the point of dynamic polymorphism and virtual functions), its hello function needs to be able to take any object of type A. A hello function which only takes objects of type B, rather than any A, is more restrictive. It can't play the role of A's hello function, so it's not an override.
If you experiment a bit with calling hello on A and B, passing objects of type A or B, you should be able to see the difference. A has a function taking an A (which you haven't defined, so if you call it then your program will fail to link, but you can fix that). B has a function taking a B. They happen to have the same name, and of course since B derives from A, you can pass a B to the function taking an A. But B's function doesn't act as an override in virtual calls.
It is possible to call A's function on a B object, but only via a reference or pointer to A. A feature of C++ is that the definition of hello in B hides the definition in A. If overloading is what you want, it's possible to un-hide the base class function by adding using A::hello; to class B. If overriding is what you want, you have to define a function taking the same parameters. For example:
#include <iostream>
class A
{
public:
virtual int hello(A a) {std::cout << "A\n"; }
virtual int foo(int i) { std::cout << "A::Foo " << i << "\n"; }
};
class B : public A
{
public:
using A::hello;
// here's an overload
int hello(B b){ std::cout << "B\n"; };
// here's an override:
virtual int foo(int i) { std::cout << "B::Foo " << i << "\n"; }
};
int main() {
A a;
B b;
a.hello(a); // calls the function exactly as defined in A
a.hello(b); // B "is an" A, so this is allowed and slices the parameter
b.hello(a); // OK, but only because of `using`
b.hello(b); // calls the function exactly as defined in B
A &ab = b; // a reference to a B object, but as an A
ab.hello(a); // calls the function in A
ab.hello(b); // *also* calls the function in A, proving B has not overridden it
a.foo(1); // calls the function in A
b.foo(2); // calls the function in B
ab.foo(3); // calls the function in B, because it is overridden
}
Output:
A
A
A
B
A
A
A::Foo 1
B::Foo 2
B::Foo 3
If you take away the using A::hello; line from B, then the call b.hello(a); fails to compile:
error: no matching function for call to `B::hello(A&)'
note: candidates are: int B::hello(B)
A bunch of good answers telling you WHAT happens, I thought I'd jump in with WHY.
There's this thing called the Liskov Substitution Principle, which says that the function in the subclass has to work under the same preconditions and postconditions as the base class. In this case, the function has to be able to operate on any object of type A. Note that because of the inheritance relationships, every B is-a A, but not every A is-a B. So to replace the base method, the new function in the derived class can weaken the preconditions or strengthed the postconditions, but not strengthen preconditions or weaken postconditions.
Your attempt to override strengthens the precondition, it accepts Bs, not all As.
Note that covariance IS allowed on return types. If your base class returned A, then it guarantees that the return value is-a A. The base class could then return a B, because every B is-a A.
But for input parameters, only contravariance meets the theoretical requirements of the LSP, and in/out parameters are invariants. In C++ in particular, all parameter types are invariant for the purposes of overloading.
First, A is not an abstract class in your code. It must have at least one pure virtual function to be abstract.
different parameters means completely different method, even though the name is the same. Think of it as a different name. That's why it's called "signature". If A would be an abstract class, this code would not compile at all.
A::hello() will be called. No problem with that, and parameter must be type A, as if there was no inheritance.
When you override a method, it redefines what the method will do. You can only override virtual members that are already defined (with their set of parameters). If the type is of A, the method on A will be called. If the type is of B, the method on B will be called even if the variable is typed A but contains an instance of type B.
You can't change the parameter definitions for an overridden method, or else it would cease to be an override.
What you are doing there is overloading not overriding, i.e. it's as if class B is:
class B
{
public:
int hello(A a) {...}
int hello(B b) {...}
};
You have two functions of the same name, but with different signatures, which makes them different functions (just like the standard library has different functions for abs(float) and abs(double) etc.)
If you want to override, then you need to have the same signature, i.e. class B's hello needs to take a parameter of type A. That way, when you call hello on an object of class B, it will use class B's hello rather than class A's.
If you actually want class B's hello to only accept objects of type B then what you have is fine, although you probably want to make class A's hello non-virtual as you are not really wanting to override it -- you are defining a new function with new parameters and new behaviour.
Thansk for the answers, but I have to clarify some things to get my final answer.
Suppose I have the A class exactly how I defined it in the original question. And I add another method:
class A {
...
int yeah();
}
Then I define class B as the following:
class B : public A {
int hello(A a);
};
And another class C analogous to B.
What I know because I'm the programmer, it's that the hello methods of B and C are gonna have obviously A type objects as parameters, but instances of the same class.
In example:
B b;
b.hello(some_other_b_instance);
or
C c;
c.hello(some_other_c_instance);
The problem is that in each hello function of the classes B and C, I want to do particular things with atributes of the particular class B or C. And because of the parameter is of type A, I cannot use them.
What I would need it's a kind of inverse polymorphysm, but its wrong because by definition I can send a C instance to B hello class, but I know it's not gonna happend.
I hope you get the idea of the code... A clase is abstract, and the real work makes sense in the particular clases B and C, each one do the work in their particular way to make the yeah function work. But B and C need to access their members to do the hello work correctly.