Member Pointer to Base Class - c++

all. I can't undestand why the bellow code need a cast to work. Someone can explain it?
class Base {
};
class Derived : public Base {
};
class Class {
public:
Derived member;
};
...
Derived obj;
Base *ptrObj = &obj; // ok, no cast needed
Derived Class::* ptr = &Class::member; // ok
Base Class::* ptr = &Class::member; // wrong, need cast, why?

Because if Base were allowed (covariant), you could then do this, which is a no-no:
Base Class::* ptr = &Class::member;
Class obj;
obj.*ptr = Base(); // <-- assigned a Base into a Derived field?!
At the same time, pointers-to-members cannot be contravariant either, because otherwise you could do this, which is also a no-no:
struct Class2 {
Base member;
};
Derived Class2::* ptr2 = &Class2::member;
Class2 obj2;
obj2.member = Base();
Derived& d = obj2.*ptr2; // <-- assigned a Base into a Derived
So, pointers-to-members are neither covariant nor contravariant, but are invariant: the type must match exactly.

Ok, I got your point Chris, but your first example works for ordinary pointers. Why should it not work for member pointers too? See the code bellow.
Derived obj;
Base *ptr = &obj;
*ptr = Base(); // it's weird, but ok
The second example will not work even for ordinary pointers, since downcasting is not allowed without cast. So I don't think that should be a explanation.

Related

Derived pointer to Base pointer conversion using static_cast, dynamic_cast,or explicit conversion won't call the base function

I have the following code.
#include <iostream>
using namespace std;
class Base
{
public:
virtual int f(){cout<<"Base"<<endl;}
};
class Derived:public Base
{
public:
int f(){cout<<"Derived"<<endl;}
};
int main()
{
Base b;
Derived d;
b.f(); ///base
((Base)d).f(); ///base
cout<<"----------------"<<endl;
Base *b1 = new Base;
Base *b2 = new Derived;
Derived *d1 = new Derived;
b1->f(); ///base
((Base*)d1)->f(); ///derived
((Base*)b2)->f(); ///derived
static_cast<Base*>(d1);
d1->f();///derived
static_cast<Base*>(b2);
b2->f();///derived
cout<<"----------------"<<endl;
Base *b5 = dynamic_cast<Base*>(b2);
Base *b6 = dynamic_cast<Base*>(d1);
if(b5)
b5->f(); ///derived
if(b6)
b6->f(); ///derived
return 0;
}
I want to ask why the derived *d1 OR b2 pointers when converted to a base using explicit cast (Base), static cast (static_cast(d1)) or dynamic cast (dynamic_cast(d1)) won't call the f() function of the base class after the conversion. It seems to call the f() function from the derived class every single time.
Also, strangely when I declare the objects this way. The conversion works and calls the base function.
Base b;
Derived d;
b.f(); ///base
((Base)d).f(); ///base
Now, I understood that the right way to access the f() from Base class would be d->Base::f(), but why should I use dynamic_cast or static_cast since they won't convert the derived pointer to base and call the right function. I would need a detailed explanation if possible. Thanks!
Fortunately, for both you and my keyboard, the explanation is trivial:
((Base)d) slices the object d to a value-copied Base instance.
((Base*)d1)->f() will still call the derived method since Base and therefore Derived are polymorphic types, and although ((Base*)d1) is a Base* pointer, it is pointing to a Derived object. Ditto for static_cast and dynamic_cast.
Reference: https://en.wikipedia.org/wiki/Object_slicing

Get derived type via base class virtual function

I am trying to get the derived type of an object via a base class virtual function. I have written this, which does not compile:
struct base {
virtual base& get_this() {
return *this;
}
};
struct derived : base {
virtual derived& get_this() override {
return *this;
}
void fn();
};
int main () {
base* pd = new derived();
derived& x = pd->get_this(); /*ERROR*/
x.fn();
return 0;
}
... giving me an error that: I cannot initialize a derived& from a base. Since get_this is virtual, why does pd->get_this() return a base& instead of a derived&? Thanks in advance!
EDIT:
Thanks everyone for their useful answers and apologies for my late reply. I should have specified in the original post that I am also interested in a solution to my problem rather than just figuring out why the above does not compile. My main problem is that fn is unique to the derived class and cannot be called via the base class. Using casts sure solves the problem but I hate writing code with if else constructs just to get the right type (also Scott Meyers advise against casts :)) . The answers seem to indicate that casts are the way to go, which in a way is at least reassuring that I am not neglecting a more 'elegant' solution to my problem. Thanks again!
C++ covariant return types support will only work, as long you already know the derived type. To downcast a base class to a possibly derived class, simply use dynamic_cast<derived>(base_ref) to determine if base_ref matches the actual derived type:
int main () {
base* pd = new derived();
derived& x = dynamic_cast<derived&>(*pd); // Will throw an exception if pd
// isn't a 'derived'
x.fn();
return 0;
}
Or alternatively:
int main () {
base* pd = new derived();
derived* x = dynamic_cast<derived*>(pd); // Will return nullptr if pd isn't
// a 'derived'
if(x) {
x->fn();
}
else {
// dynamic_cast<derived*> failed ...
}
return 0;
}
c++ supports covariant return types for derived classes, but as the other answers describe you cannot get it via calling the base class (pd->get_this()) here.
You might also consider static polymorphism to check type compliance at compile time, if you can't use RTTI, exception handling or want tight type binding (without vtable overhead).
The static type of pd is base *. Thus, when the compiler looks for the member function get_this(), it finds only base::get_this(). The return type of base::get_this() is base&, which is not convertible to derived&. Hence the error.
I would like to add to Novelocrat's answer by referring you to section 10.3, paragraph 8 of the working draft C++ standard (click here) which explains in which case the returned pointer's static type is Derived* as opposed to Base*. Basically, if you would have called get_this() through a pointer to the dervied class then you would have gotten the right type with no compiler error.
Here is a quote from the standard along with an example (also from the standard):
If the return type of D::f differs from the return type of B::f, the
class type in the return type of D::f shall be complete at the point
of declaration of D::f or shall be the class type D. When the
overriding function is called as the final overrider of the overridden
function, its result is converted to the type returned by the
(statically chosen) overridden function (5.2.2). [Example:
class B { };
class D : private B { friend class Derived; };
struct Base {
virtual void vf1();
virtual void vf2();
virtual void vf3();
virtual B* vf4();
virtual B* vf5();
void f();
};
struct No_good : public Base {
D* vf4(); // error: B (base class of D) inaccessible
};
class A;
struct Derived : public Base {
void vf1(); // virtual and overrides Base::vf1()
void vf2(int); // not virtual, hides Base::vf2()
char vf3(); // error: invalid difference in return type only
D* vf4(); // OK: returns pointer to derived class
A* vf5(); // error: returns pointer to incomplete class
void f();
};
void g() {
Derived d;
Base* bp = &d; // standard conversion:
// Derived* to Base*
bp->vf1(); // calls Derived::vf1()
bp->vf2(); // calls Base::vf2()
bp->f(); // calls Base::f() (not virtual)
B* p = bp->vf4(); // calls Derived::pf() and converts the
// result to B*
Derived* dp = &d;
D* q = dp->vf4(); // calls Derived::pf() and does not
// convert the result to B*
dp->vf2(); // ill-formed: argument mismatch
}
C++ supports covariant return type.
What it means is that when you call get_this() on a derived object through a base pointer it is the implementation of derived that is going to be called.
However this does not mean that calling base::get_this will give you a derived&. The return type of base::get_this is base&. if you want to get a derived object you will have to call get_this through a derived pointer (or downcast your base& to a derived&). Note that this is how return type covariance work in Java, C++, D...
base* pbase = new base();
base* pderived = new derived();
derived* pderived2 = new derived();
base& a = pbase->get_this(); // call implementation in base, return base&
base& b = pderived->get_this(); // call implementation in derived, return base&
derived& c = pderived2->get_this(); // call implementation in derived, return derived&
I found a simple solution, but if is possible, I would the masters to evaluate:
class base{
type = 1;
virtual int getType() final {
return type;
}
}
class derived1 : public base {
derived1(){
type = 2;
}
}
This way, you can call the method 'int getType()' of any of derived classes. As the type is set on the constructor, there is no risk of misbehaviour.
To enhance the usability, i've created a predefined 'types'.
I'm using, but I don't know if is MacGyvery!

Casting to base class validity

Say I have a class named Base and a class that derives from it called SuperBase. Given that add takes in a Base*, would either of these be valid:
SuperBase *super = new SuperBase;
bases.add(super);
Or
SuperBase *super = new SuperBase;
bases.add((Base*)super);
The first works as long as SuperBase publicly derives from Base, via an implicit conversion from derived-to-base:
struct base { virtual ~base() {} };
struct derived : base {};
base* b = new derived; // okay
The second works as well, but ignores the protection of Base:
struct derived : private base {}; // private base
base* b = new derived; // not okay, base is private
base* b = (base*)(new derived); // okay, but gross
If it's private, you probably shouldn't cast to it.
Both are valid - a child can be used in a place where a reference/pointer to parent is expected. This is called polymorphism.
Both would be valid, though type-casting super to Base* is not necessary.

Why does this fail without static_cast?

Compiling f works, but compiling g fails with an error.
Why does this happen?
class A {
public:
A() {}
};
class B : public A {
public:
B() {}
};
void f() {
A* a = new A();
B* b = static_cast<B*>(a);
}
void g() {
A* a = new A();
B* b = a;
}
A static_cast forces a conversion that is potentially unsafe.
B* b = static_cast<B*>(a);
This would be valid if a pointed to an A object that actually was the base class sub-object of a B object, however it doesn't. The cast forces the conversion.
B* b = a;
There is no cast here and there is (correctly) no implicit conversion allowed from base class pointer to derived class pointer. A pointer to a derived class can always be converted to a pointer to a base class because a derived class object always contains a base class sub-object but not every base class instance is a sub-object of a particular derived class type.
Well, yeah. Doing:
B* b = new A();
Is unsafe. You end up with a B pointer to an A object; you never construct the B portion of the object; your object is "sliced".
On the other hand...
A* a = new B();
...would be fine.
You are trying to convert a pointer from A* to B*. I am not sure what you are trying to achieve. But since B* is derived from A* and not the other way around this is not valid. Maybe you want to do something like this:
int main()
{
///The above code compiles while if I replace above two line in main with below assignment it gives error.
A *a=new A();
A * b=new B();
}
Yes, it does give an error if you want to assign a base class to a derived class pointer type. No, it doesn't give an error if you explicitly cast the pointer type, because in C++ you are allowed to shoot yourself in the foot if you so desire.
What exactly is baffling you, or what did you expect to achieve with your code?
A base class cannot be implicitly converted to a derived class. Just consider this
class A {
public: int x;
};
class B : public A {
public: int y;
};
B* b = new A; // assume it works, so 4 bytes is allocated and initialized.
b->y; // bam! accessing unallocated region.

Can a pointer of a derived class be type cast to the pointer of its base class?

The pointer of derived class returned by new can be type cast to the pointer of its base class.
Is this true or false?
I know dynamic_cast can be used to cast downside. Generally, how to cast a pointer of derived class to a pointer of its base class?
Yes. Conversion from a pointer to a derived class to a pointer to a base class is implicit. Thus, the following is perfectly fine:
struct B { };
struct D : B { };
D* my_d_ptr = new D;
B* my_d_ptr_as_a_b_ptr = my_d_ptr;
Casting a pointer to a derived to a pointer to base should be implicit. This is the whole point of polymorphism: An instance of a derived class should always be safely usable as an instance of the base class. Therefore, no explicit cast is necessary.
That's true if derived class inherits 'publicly' and 'non-virtually' from base:
You can't convert Derived* to Base* neither implicitly nor using static_cast/dynamic_cast (C-cast will do the job, but you should think twice before use this hack!);
class Base { };
class Derived : protected Base { };
int main()
{
Base* b = new Derived(); // compile error
}
Also don't work if base class is ambiguous:
class Base { };
class Derived1 : public Base { };
class Derived2 : public Base { };
class MostDerived : public Derived1, Derived2 { };
int main()
{
Base* b = new MostDerived(); // won't work (but you could hint compiler
// which path to use for finding Base
}
Edit: added code samples, added ambiguous use case, removed virtual inheritance example.
Your question is not clear becuse it mixes up several different things.
On the one hand, a pointer to derived class can be converted to a pointer to a base class (assuming the base is accessible). This is a natural conversion and there's no need for any cast to do it.
On the other hand, you mention dynamic_cast and its ability to to perform downcasts. But downcasts are casts in the opposite direction: from a pointer to a base class to a pointer to derived class. Downcasts can be performed by both dynamic_cast and static_cast depending on what you need and what amount of run-time checking you require.
... And at the same time you ar talking about casting the result of new, which, of course, can only be upcasted, but not downcasted.
So, what is it you are asking about? Upcasts or downcasts?