I am trying to understand the following bit of code:
#include<iostream>
using namespace std;
class Base {
public:
virtual void f(float) { cout << "Base::f(float)\n"; }
};
class Derived : public Base {
public:
virtual void f(int) { cout << "Derived::f(int)\n"; }
};
int main() {
Derived *d = new Derived();
Base *b = d;
d->f(3.14F);
b->f(3.14F);
}
This prints
Derived::f(int)
Base::f(float)
And I am not sure why exactly.
The first call d->f(3.14F) calls the function f in Derived. I'm not 100% sure why. I had a look at this (http://en.cppreference.com/w/cpp/language/implicit_cast) which says:
A prvalue of floating-point type can be converted to prvalue of any integer type. The fractional part is truncated, that is, the fractional part is discarded. If the value can not fit into the destination type, the behavior is undefined
Which to me says you can't do this since a float does not fit into an int. Why is this implicit conversion allowed?
Secondly, even if I just accept the above as being OK, the 2nd call to b->f(3.14F) doesnt make sense. b->f(3.14F) is calling a virtual function f, so this is dynamically resolved to call the f() associated with the dynamic type of the object pointed to by b, which is a Derived object. Since we are allowed to convert 3.14F into an int, because the first function call indicates that this is legal, this (to my understanding) should call the Derived::f(int) function again. Yet it calls the function in the Base class. So why is this?
edit: here's how I figured it out and explained it to myself.
b is a pointer to a Base object, therefore we can only use b to access members of a Base object, even if b really points to some Derived object (this is standard OO/inheritance stuff).
The only exception to this rule is when a member function of Base is declared as virtual. In such a case, a Derived object may override this function, providing another implementation by using the exact same signature. If this occurs, then this Derived implementation will be called at run time even if we happen to be accessing the member function through the pointer to a Base object.
Now, in the snippet of code above, we do not have any overriding taking place because the signatures of B::f and D::f are different (one is a float, the other an int). So when we call b->f(3.14F), the only function that is considered is the original B::f, which is what is called.
The two function have different signatures, so f in derived does not override the virtual function in base. Just because the types int and float can be implicitly cast does not have an effect here.
virtual void f(float) { cout << "Base::f(float)\n"; }
virtual void f(int) { cout << "Derived::f(int)\n"; }
A clue to what is happening can been seen with the new override keyword in C++11, this is very effective at reducing these sort of bugs.
virtual void f(int) override { cout << "Derived::f(int)\n"; }
from which gcc produces the error:
virtual void Derived::f(int)’ marked override, but does not override
clang
error: 'f' marked 'override' but does not override any member functions
http://en.cppreference.com/w/cpp/language/override
EDIT:
for your second point, you can actually expose the float overload from base in derived which exposes an implicitly compatible member function. like so:
class Derived : public Base {
public:
using Base::f;
virtual void f(int) { cout << "Derived::f(int)\n"; }
};
Now passing a float to member function f binds closer to the function defined in the base and produces:
Base::f(float)
Base::f(float)
Easy way to think about hiding is as follows - look at the line d->f(3.14F); from the example:
First step for compiler is to choose a class name. Member function name f is used to do this. No parameter types is used. Derived is chosen.
Next step for compiler is to choose a member function from that class. Parameter types are used. void Derived::f(int); is the only matching function with correct name and parameters from class Derived.
Narrowing type conversion from float to int is happening.
As the argument types of these two functions differ, the one in Derived class does not actually override the one from the Base. Instead Derived::f hides Base::f (don't have the standard with me at the moment, so I can't quote the chapter).
This means that when you call d->f(3.14f), the compiler doesn't even consider B::f. It resolves the call to D::f. However, when you call b->f(3.14f), the only version which compiler can choose is B::f as D::f doesn't override it.
Your reading of If the value can not fit into the destination type, the behavior is undefined is wrong. It says value not type. So value 3.0f does fit into int, but 3e11 doesn't. In the latter case, the behavior is undefined. The first part of your quote, A prvalue of floating-point type can be converted to prvalue of any integer type. explains why d->f(3.14f) is resolved to D::f(int) - float indeed can be converted to integer type.
Related
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/
I'm experimenting with this code
class Base
{
public:
virtual void foo(int) {
// void foo(int) {
cout << "int" << endl;
}
};
class Derived : public Base
{
public:
void foo(double) {
cout << "double" << endl;
}
};
int main()
{
Base* p = new Derived;
p->foo(2.1);
Derived d;
d.foo(2); // why isn't this double?
return 0;
}
It's also available in online editor here https://onlinegdb.com/s8NwhfG_Yy
I'm getting
int
double
I don't understand why d.foo(2) calls the double version. Since Derived inherits the int method and has its own double method, wouldn't polymorphism dictate that 2 be matched to the parent? Thanks.
Polymorphism as in calling virtual methods is orthogonal to symbol and overload resolution. The former happens run-time, the rest at compile-time.
object->foo() always resolves the symbol at compile-time - member variable with overloaded operator() or a method. virtual only delays selecting "the body" of the method. The signature is always fixed, including the return value of course. Otherwise the type system would break. This is one of the reasons why there cannot be virtual function templates.
What you are actually experiencing is name hiding and overload resolution.
For Base* p; p->foo(2.1), the list of possible symbol candidates is only Base::foo(int). The compiler cannot know that p points to Derived (in general) because the choice must be done at compile time. Since int is implicitly convertible to double, foo(int) is chosen. Because the method is virtual, if Derived were to provide its own foo(int) override, it would have been called instead of Base::foo(int) at run-time.
For Derived*d; d->foo(2), the compiler first looks for symbol foo in Derived. Since there is foo(double) which is valid as foo(2), it is chosen. If there was no valid candidate, only then the compiler would look into base classes. If there was Derived::foo(int), possibly virtual, the compiler would choose this instead because it is a better match.
You can disable the name hiding by writing
class Derived: public Base{
using Base::foo;
};
It injects all Base::foo methods into Derived scope. After this, Base::foo(int) (now really Derived::foo(int)) is chosen as the better match.
Read Overload resolution. The answer is in Details:
If any candidate function is a member function (static or non-static), but not a constructor, it is treated as if it has an extra parameter (implicit object parameter) which represents the object for which they are called and appears before the first of the actual parameters.
The selecting between the methods void foo(int) and void foo(double) is happening like between the functions void foo(Base, int) and void foo(Derived, double). The second function while calling d.foo(2) is ranked higher.
If you add using Base::foo; in Derived, the resolution will be performed between three functions void foo(Base, int), void foo(Derived, int) and void foo(Derived, double), and int will be printed.
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/
This is basically a copy from the example given in Item 21. Overriding Virtual Functions in Herb Sutter's book Exceptional C++.
#include <iostream>
#include <complex>
using namespace std;
class Base
{
public:
virtual void f(int);
virtual void f(double);
virtual ~Base() {};
};
void Base::f(int) { cout << "Base::f(int)" << endl; }
void Base::f( double ) { cout << "Base::f(double)" << endl; }
class Derived: public Base {
public:
void f(complex<double>);
};
void Derived::f(complex<double>) { cout << "Derived::f(complex)" << endl; }
int main()
{
Base* pb = new Derived;
pb->f(1.0);
delete pb;
}
The code prints Base::f(double) and I have no problems with that. But I couldn't understand the explanation given by the author on the top of page 122 (emphasis is mine):
Interestingly, even though the Base* pb is pointing to a Derived
object, this calls Base::f( double ), because overload resolution is
done on the static type (here Base), not the dynamic type (here
Derived).
My understanding is that the call pb->f(1.0) is a virtual call and Base::f(double) is the final overrider for f(double) in Derived. What does that have to do with function overloading?
The delicate part here is that virtual methods are a mechanism to dispatch the function call, while overloading is a feature that affects the resolution of the call.
That is, for any call the compiler needs to figure out which method should be called (resolve it); afterwards, and in a logically distinct operation, it needs to generate code that calls the correct implementation of that method (dispatch it).
From the definitions of Base and Derived given above we can easily reason that if f(double) is called on a Base* then the call should be dispatched to any derived override (if applicable) in preference to the base implementation. But answering that is answering a question totally different than
When the source says pb->f(1.0), which of the methods named f
should be used to resolve the method call?
As Sutter explains, the spec says that when resolving the call the compiler will look at the methods named f declared on the static type pointed to by pb; in this case, the static type is Base* so overloads (not overrides!) declared on Derived will not be considered at all. However, if the method that the call resolves to is virtual then the possible implementation provided on Derived will be used as expected.
The reason this example is interesting is because, if pb were a Derived* instead of a Base*, or if the compiler could somehow use the dynamic type rather than the static type for performing overload resolution, it would match the call to pb->f(1.0) to void Derived::f(complex<double>) (complex<double> can be implicitly constructed from double). This is because the presence of a function named f in a derived class effectively hides any base class overloads with the same name, even if their argument lists are different. But since the static type of pb is actually Base*, this doesn't happen.
In this example, despite the repeated occurrence of virtual, there is no method overriding at all; the method f in the derived class overrides neither of the ones in the base class, because the argument types do not match. Given this circumstance, there is no way the call of pb->f could ever invoke the (unique) method Derived::f. Overload resolution/name lookup (which only considers methods of the static type of pb->f) must choose between the two methods declared as Base::f, and in the example it will choose the one with argument type double. (At runtime this might end up calling an override, if one is defined in a different derived class than Derived, and if the example is modified so that pb could possibly point to an object of such another derived class.)
A separate issue is that the methods named f in Base and Derived would not be simultaneously considered for overload resolution if f is called from an expression of (static) type Derived either, this time because the methods in the base class are hidden by the declaration of f in Derived, so they are not available for such calls. I think this hiding can be avoided by declaring using Base::f;,which "lifts" the methods Base::f into Derived as if they were also declared there (but I admit not knowing the details of this mechanism; I suppose the lifts would then be overrides of the virtual base method with the same argument type, but it makes little difference because the lifts refer to the implementation in the base class anyway.)
I read below statements in Addison Wesley FAQs.
Beware: passing objects by value can
be dangerous in some situations. Often
it is better to pass objects by
reference-to-const than to pass them
by value. For example, pass-by-value
won't work if the destination type is
an abstract base class and can result
in erroneous behavior at runtime if
the parameter's class has derived
classes. However if the class of the
parameter is guaranteed not to have
derived classes, and if the function
being called needs a local copy to
work with, pass-by-value can be
useful.
How it can be erroneous behavior at runtime if destination type is an Abstract class and if the parameter's class has derived class ?
Does copy constructor solve this problem ? If so, how ? Thank you.
EDIT: So, should the statement above be "erroneous behavior at compile time" ? Not "runtime" .
There are two things here, and you seem to be mixing them up:
1) If the destination type is an abstract class and you pass by value, there will be no runtime error: it won't compile, period.
2) When there will be a runtime error is if the destination type is concrete but has derived classes. Consider this example:
#include <iostream>
struct A {
virtual void f() { std::cout << "A\n"; }
};
struct B : public A {
virtual void f() { std::cout << "B\n"; }
};
void func(A a){
a.f();
}
int main() {
func( B() );
}
It compiles fine, but outputs A. If you change the parameter to A&, polymorphism works as expected and B is printed. This problem is known as slicing, and there's a nice question here on SO about it.
As a general rule of thumb, if the class has any virtual functions you should a) declare the destructor as virtual and b) avoid passing it by value.
I actually avoid passing any parameter by value, unless I'm sure it's a primitive type such as an int. If you're writing a template, you're better off accepting all parameters as const T&.
How it can be erroneous behavior at runtime if destination type is an Abstract class and if the parameter's class has derived class ?
It won't even compile. "Pass by value" means that you would try to copy the abstract class parts of the argument to a new object. But you cannot create an object with an abstract class as its mosted derived class because it's abstract.
It won't even compile..
From Standard docs 10.4.3,
An abstract class shall not be used as a parameter type, as a function return type, or as the type of an explicit conversion.
Pointers and references to an abstract class can be declared.
OK, suppose we have the following hierarchy
#include <iostream>
class Base
{
public:
Base(): BaseNum(42)
{}
virtual void f()
{
std::cout << BaseNum;
}
virtual ~Base()
{
}
protected:
int BaseNum;
};
class Derived: public Base
{
public:
Derived(): DerivedNum(14)
{}
virtual void f()
{
std::cout << DerivedNum + BaseNum;
}
private:
int DerivedNum;
};
If you have a function taking Base by value and you pass Derived instead, the derived object is first converted to base(by truncating any specific fo Derived information, like DerivedNum) and then copied into the parameter. Thus, in the function you will LOSE any information that was specific to derived. This is known as slicing. Inside the function, if you called the virtual f() function, the Base::f would be called no matter Derived was passed.
If you passed by reference or pointer, no copying would be done. And converting from Derived& to Base& preserves the dynamic type information because no actual copying and therefore slicing is done. Sure, you couldn't use the Base& as Derived&, but if you called the f, Derived::f would be called.
If Base were abstract, then it would be a compiler error, because when you pass by value, the parameter must be instantiated via a copy constructor, and, as we know, an abstract class cannot be instantiated.
HTH.
I doubt passing derived class instances by value to be possible at all. After all the size of the passed value must be known at compile time. However the size of the abstract base class and the derived class can (and most likely will) differ. The resulting behavior is pretty much undefined.