Say i have this part of code:
#include<iostream>
using namespace std;
class A {
public:
virtual int f(const A& other) const { return 1; }
};
class B : public A {
public:
int f(const A& other) const { return 2; }
virtual int f(const B& other) const { return 3; }
};
void go(const A& a, const A& a1, const B& b) {
cout << a1.f(a) << endl; //Prints 2
cout << a1.f(a1) << endl; //Prints 2
cout << a1.f(b) << endl; //Prints 2
}
int main() {
go(A(), B(), B());
system("pause");
return 0;
}
I can understand why the first two will print 2. But I cannot understand why the last print is also 2. Why doesn't it prefers the overloaded function in B?
I already looked at this and this but I couldn't manage to understand from these.
int B::f(const B& other) const doesn't override int A::f(const A& other) const because the parameter type is not the same. Then it won't be called via calling f() on reference of the base class A.
If some member function vf is declared as virtual in a
class Base, and some class Derived, which is derived, directly or
indirectly, from Base, has a declaration for member function with the
same
name
parameter type list (but not the return type)
cv-qualifiers
ref-qualifiers
Then this function in the class Derived is also virtual (whether or
not the keyword virtual is used in its declaration) and overrides
Base::vf (whether or not the word override is used in its
declaration).
If you use override specifier (since C++11) compiler will generate the error.
class B : public A {
public:
int f(const A& other) const { return 2; }
virtual int f(const B& other) const override { return 3; }
};
Such as Clang:
source_file.cpp:10:17: error: 'f' marked 'override' but does not override any member functions
virtual int f(const B& other) const override { return 3; }
^
If you add an overload for it in the base class, you might get what you want. Note that a forward declaration of class B will be needed.
class B;
class A {
public:
virtual int f(const A& other) const { return 1; }
virtual int f(const B& other) const { return 1; }
};
LIVE
It's easy, really. You're calling f on an object with static type A. A has only one f, so there's only one entry in the vtable for that function. Overload resolution takes place compile-time. The overload will only be resolved if you call it on an object whose static type is B
The confusion comes in that your:
int f(const A& other) const { return 2; }
line is actually virtual also and is overriding your line:
virtual int f(const A& other) const { return 1; }
Meanwhile, the line:
virtual int f(const B& other) const { return 3; }
ends up being completely ignored because everything matches to the "return 1" line, then follows polymorphically up the chain to the "return 2" line. As the other poster said, the const B portion means it won't match the polymorphic method call.
As an aside: If you're getting a 2 on the first line, I'm suspicious of undesired stack behavior. I'd expect a 1. Perhaps try allocating like this:
A a1;
B b1, b2;
go(a1, b1, b2);
Related
I have the following problem. A class E overrides two methods: one comes from an abstract class D which inherits from a specialization of a templated class C<T> where T = A. The other is from C<A> directly. And both have the same name.
Now, D should have access to both methods: doSomething(const A& a) because it inherits it from C, and doSomething(const B& b) because D declares it.
However, the following code does not compile, because the compiler recognizes only the method doSomething(const B&) from the pointer to D
#include <iostream>
#include <sstream>
using namespace std;
class A {
private:
int a = 10;
public:
inline std::string hello() const{
std::stringstream ss;
ss << a;
return ss.str();
}
};
class B {
private:
int b = 20;
public:
inline std::string hello() const{
std::stringstream ss;
ss << b;
return ss.str();
}
};
template <class T>
class C {
public:
inline virtual bool doSomething(const T& t) {
std::cout << "C is doing something with T" << t.hello() << std::endl;
return true;
}
};
class D : public C<A> {
public:
virtual void doSomething(const B& b) = 0;
};
class E : public D {
public:
inline bool doSomething(const A& a) override {
std::cout << "E is doing something with A: " << a.hello() << std::endl;
return true;
}
inline void doSomething(const B& b) override {
std::cout << "E is doing somethign with B: " << b.hello() << std::endl;
}
};
int main()
{
A a;
B b;
D* d = new E();
d->doSomething(b);
d->doSomething(a); // compile error, does not recognize doSomething(const A&)
delete d;
}
The compiler shows the following error:
In function ‘int main()’:
main.cpp:62:19: error: no matching function for call to ‘D::doSomething(A&)’
d->doSomething(a); // compile error, does not recognize doSomething(const A&)
^
main.cpp:39:18: note: candidate: virtual void D::doSomething(const B&)
virtual void doSomething(const B& b) = 0;
^
main.cpp:39:18: note: no known conversion for argument 1 from ‘A’ to ‘const B&’
Why is that?
This happens because compiler does not automatically merge all the functions from the base classes to the set of overloaded functions to select from. That is the set of overloaded functions considered when you call d->doSomething(a); consists only of D::doSomething(const B& b). In order to fix this you need to bring C<A>::doSomething; into D class
class D : public C<A> {
public:
using C<A>::doSomething;
virtual void doSomething(const B& b) = 0;
};
To make a base class function visible in the derived class which is hidden by a function of the same name, you should use the using directive (using C<A>::doSomething;).
However, I also want to add that GCC 8.2 reports that there is undefined behavior in the code when you delete d. This is because the class D has a non-virtual destructor.
warning: deleting object of abstract class type 'D' which has non-virtual destructor will cause undefined behavior [-Wdelete-non-virtual-dtor]
delete d;
See demo here.
In C++11 it is possible to make a public member of a private base class accessible to the outside (public) with a using declaration. For example
class A {
private:
int i = 2;
public:
void f() { i = 3; }
friend bool operator==(const A& l, const A& r) { return l.i == r.i; }
};
class B : private A {
public:
using A::f;
};
int main() {
B b, b2;
b.f();
}
b.f() is possible because of the using A::f in the definition of B.
Is it possible write a similar declaration which would make the up-cast from B& to A& possible for the friend function operator==(A&, A&), so that b == b2 can be called in main()?
No, only B can internally cast itself to A, and it otherwise is not possible because from a client's perspective B is not an A but rather has an A
Even if you replaced your friend bool operator= with a member function equals:
class A {
private:
int i = 2;
public:
void f() { i = 3; }
bool equals(const A& r){return i == r.i;}
};
class B : private A {
public:
using A::f;
using A::equals;
};
While this compiles, you cannot ever call b.equals(b2) because no implicit conversion is ever possible from a type of B to a type of A from the caller's perspective (due to private inheritance) .
You'll need to provide your own operator== or change your inheritance to public or protected. Here's an example where B declares its own friend bool operator==
class B : private A {
public:
using A::f;
friend bool operator==(const B& l, const B& r)
{
return (static_cast<A>(l) == static_cast<A>(r)) && true;
// "true" is a stand-in for some other condition
}
};
Read more at isocpp
Edit:
If you really want to play games, you will notice that I said no implicit conversion is ever possible, but some explicit conversions are. Because B does technically derive from A you can do pointer casting to make it work, but I don't recommend it:
class A {
private:
int i = 2;
public:
void f() { i = 3; }
bool equals(const A* r){return i == r->i;}
};
class B : private A {
public:
using A::f;
using A::equals;
};
int main() {
B b, b2;
b.f();
(::A*)(&b)->equals((::A*)(&b2));
}
Or you could use pointer casting's ugly cousin, reference casting, if you wish to keep the original operator== syntax
class A {
private:
int i = 2;
public:
void f() { i = 3; }
friend bool operator==(const A& l, const A& r) { return l.i == r.i; }
};
class B : private A {
public:
using A::f;
};
int main() {
B b, b2;
b.f();
((::A&)(b)) == ((::A&)(b2));
}
See §11.2 [class.access.base] for more
In the below program, the function 'f' in the base class A is hidden for the objects of derived class B. But when I call function f through const A *d which is pointing object of B, function f from the base class is getting invoked. If I remove const specifier for the pointer (i.e. A *d) function 'f' from derived class is called. My query how the constness is making a difference here ? Thanks for any help.
#include <iostream>
class A
{
public:
virtual void f(int n) { std::cout << "A::f\n"; }
virtual ~A() { }
void f(int n) const { std::cout << "A::f const\n"; }
};
class B
: public A
{
public:
void f(int n) { std::cout << "B::f\n"; }
void f(int n) const { std::cout << "B::f const\n"; }
};
int main()
{
const A a;
B b;
A &c = b;
const A *d = &b;
c.f(1);
d->f(1);
return 0;
}
Output (with const A *d):
B::f
A::f const
Output (with A* d)
B::f
B::f
The signature of the function to be called is determined on call site based on the static type of the pointer. The correct overrider of this signature is then chosen at runtime.
In other words, if you have this:
const A *d;
d->f(1);
then the f is searched for in const A. So it finds the non-virtual void f(int) const.
However, if you have this:
A *e;
e->f(1);
then the f is searched for in non-const A. So it finds virtual void f(int), which is then (at runtime) delegated to the final overrider void B::f(int).
EDIT
This follows from the rules on member function selection. When accessing through a const path (pointer or reference), only const member functions are applicable. When accessing through a non-const path, non-const functions are considered. Only if there is none is the pointer (or reference) implicitly converted to a pointer (or-reference) to const and then the const member functions are considered.
I know about the basic concept of virtual function and run-time call. But i tried
running some piece of code which confused me
class A {
public:
A& operator=(char) {
cout << "A& A::operator=(char)" << endl;
return *this;
}
virtual A& operator=(const A&) {
cout << "A& A::operator=(const A&)" << endl;
return *this;
}
};
class B : public A {
public:
B& operator=(char) {
cout << "B& B::operator=(char)" << endl;
return *this;
}
virtual B& operator=(const B&) {
cout << "B& B::operator=(const B&)" << endl;
return *this;
}
};
int main() {
B b1;
B b2;
A* ap1 = &b1;
A* ap2 = &b1;
*ap1 = 'z';
*ap2 = b2;
}
Running this program give me the following output:-
A& A::operator=(char) //expected output
A& A::operator=(const A&) //Why this Output? in case of *ap2 = b2;
b2 is an object of B type but still it goes in virtual A& operator=(const A&)
and not virtual B& operator=(const B&). Why is this so ?
Because virtual B& operator=(const B&) does not override virtual A& operator=(const A&); the arguments are different.
For a derived class function to override Base class function, the derived class function needs to have the exact same function prototype(exception: covariant return types are allowed).
The = operator in derived class B here does not have same function prototype as = in Base class A, and hence it does not override the Base class =.
The only = operator available is the one which is called.
For a function to be considered an override the signature has to match the version in tha base class exactly (well, the return type may be covariant if a pointer or a reference is returned). That is, you would need to define
B& B::operator= (A const&)
to override the version from the base class. Note that for input parameters in overriding functions it wouldn't make sense to be covariant because you can't guarantee that the base class version is called with a derived object in a context using only the base class. If anything parameters to an overriding function could be contravariant but C++ doesn't support this.
Here in the derived class the function is taking B whereas in base class its taking A. So, basically its not being overridden as the function arguments are different.
Also note that the return type in case of overriding may be different as in your case you are returning reference of A in base and reference of B in derived.
virtual Base& func(const Base&)
virtual Derived& func(const Base&)
This is valid form of overriding
Suppose:
struct A {
virtual int foo(const A& a) const { return 1; }
};
struct B : A {
virtual int foo(const A& a) const { return 2; }
virtual int foo(const B& b) const { return 3; }
};
void testOverloadingBinding(const A& a,const B& b) {
cout << a.foo(b);
}
int main() {
testOverloadingBinding(B(),B());
}
It prints 2. I would assume it prints 3 since this binding is dynamic, and as far as I know overloading has static binding.
Can anyone please explain how the compiler decides which function to invoke here?
This:
virtual int foo(const B& b) const;
is not an override for this:
virtual int foo(const A& a) const;
Therefore it can never be called via a reference to an A.
virtual int foo(const B& b) const;
doesn't override anything, so compiler chooses first function. But, probably in future, we will have a dynamic type resolution, and in this case compiler will choose second function.
For more info, see http://www2.research.att.com/~bs/multimethods.pdf
There is simply no overload available to resolve to. The object you are calling foo on is of type A and in A only one function foo(const A&) exists. The dynamic dispatching yields the function in the base class. In C++ a member function is identified by its name and its arguments. Adding an overload in a base that does not exist in the parent will not enable dynamic dispatch onto it.