C++ rules for covariant virtual function - c++

I was reading Covariant virtual function. It says that
Suppose that B::f overrides the virtual function A::f. The return types of A::f and B::f may differ if all the following conditions are met:
1) The const or volatile qualification of the pointer or reference
returned by B::f has the same or less const or volatile qualification
of the pointer or reference returned by A::f.
2) A::f returns an lvalue reference if and only if B::f returns an
lvalue reference.
3) The function B::f returns a pointer or a reference to a class of
type T, and A::f returns a pointer or a reference to an unambiguous
direct or indirect base class of T.
4) The return type of B::f must be complete at the point of declaration of B::f, or it can be of type B
Will someone explain the above 2 rules by giving suitable example? What exactly these 2 rules mean?Is the second rule is applicable from C++11?
Is the following example satisfies 1st rule I've said here?
#include <iostream>
class Base {
public:
virtual const Base& fun() const
{
std::cout<<"fun() in Base\n";
return *this;
}
virtual ~Base()
{ }
private:
int a=3;
};
class Derived : public Base
{
const Derived& fun() const
{
std::cout<<"fun() in Derived\n";
return *this;
}
};
int main(){
Base* p=new Derived();
p->fun();
delete p;
return 0;
}
Please correct me If I am wrong somewhere.I am confused in first 2 rules.
Thanks
Your help will be highly appreciated.

The first rule means that you can't make an overridden function return a B which has const or volatile if the A version does not:
struct A
{
virtual A* foo() { return new A{}; }
};
struct B : A
{
B* foo() override {return new B{}; } //valid
const B* foo() override {return new B{}; } //invalid
volatile B* foo() override {return new B{}; } //invalid
};
This makes sense if the think about the call site:
A* my_b = new B{};
A* new_b = my_b->foo(); //discards the const qualifier if B::foo() returns const B*
The second rule means that you can't have disparate reference or pointer types as covariant return types. Using the same example as above:
struct A
{
virtual A* foo() { return new A{}; }
};
struct B : A
{
B* foo() override {return new B{}; } //valid
B& foo() override {return new B{}; } //invalid
B&& foo() override {return new B{}; } //invalid
};
Again, think about the call site:
A* my_b = new B{};
A* new_b = my_b->foo(); //if B::foo() returns a reference, this line is syntactically ill-formed
Your example satisfies both rules because both return types have the same cv-qualification and are both lvalue references.

Related

C++ cast pointer to base class pointer for virtual method call

I have the following code:
struct A
{
virtual void foo() {std::cout << "A\n";}
};
struct B : public A
{
virtual void foo() {std::cout << "B\n";}
};
void bar(A * a)
{
a->foo();
}
Without changing this code, is it possible to cast bp pointer to B, so calling bar would print "A"?
int main()
{
B * bp = new B();
bar(/* do somethig*/ bp);
return 0;
}
Tried every cast I remebered:
int main()
{
B * bp = new B();
bar((A*)bp);
bar(static_cast<A*>(bp));
bar(reinterpret_cast<A*>(bp));
bar(dynamic_cast<A*>(bp));
return 0;
}
You could make a shim wrapper around B, and have the shim's virtual function dispatched to BWrap::foo() call directly to A::foo();.
There's not really any point in the example to carrying along the B& member variable reference, but for more interesting examples there may be a use case.
struct BWrap : public A
{
B& b;
BWrap(B& bb) : b{bb} {}
virtual void foo() { b.A::foo(); }
};
int main()
{
B* bp = new B();
BWrap bw{*bp};
bar(&bw);
}
If you insist on the A object being a base class subobject of a B object and on not modifying the first code snippet at all, then the only solution is to add an even more derived class that can override the virtual call as explained in the answer by #Eljay (which I completely forgot to think about when first writing this answer).
Other options are to create a complete A object, not a B object, or to modify bar to do a call without virtual dispatch by using a qualified name:
a->A::foo();
All of the casts you are showing have the same effect as the implicit conversion, except for reinterpret_cast which will cause undefined behavior when used this way.

Can I forbid calling private member of derived class from base?

I has this code:
struct A {
virtual void f() {}
};
struct B: A {
private:
void f() override {}
};
...
B b;
A& a = b;
a.f();
And ofcourse it will call f() from B cause private is checked on the compilation time but choosing virtual function version are in the runtime. Can I forbid f() calling in this case?
No. As you call f() on an A reference, the access rules are checked on A. You can't expect the compiler to check the rules for B, because it does not necessarily know that a is of type B.
From cppreference:
Access rules for the names of virtual functions are checked at the call point using the type of the expression used to denote the object for which the member function is called. The access of the final overrider is ignored:
struct B { virtual int f(); }; // f is public in B
class D : public B { private: int f(); }; // f is private in D
void f() {
D d;
B& b = d;
b.f(); // OK: B::f is public, D::f is invoked even though it's private
d.f(); // error: D::f is private
}
Your code should not rely on this though. In my opinion, access specifiers for a given method should be consistent across the class hierarchy.

What's the right behavior when auto meets polymorphism and virtual function?

class B {
public:
virtual void f(){
printf("B\n");
}
};
class D : public B {
public:
void f() {
printf("D\n");
}
};
int main(void)
{
B* d = new D();
d->f();
auto b = *d;
b.f();
}
for d->f();, the output is D. this is right.
But for b.f();, the output is B. Is this right?
Is this right?
It's right, the type is deduced at compile time. auto uses the same rules of template argument deduction for type deduction, based on the static type, dynamic polymorphism won't be considered.
For this case, the type of d is B*, then type of *d is B, so the type of b is just B. Then *d will be slicing copied to b, for b.f() B::f() should be invoked.
The code is equivalent to the following which might be clearer.
B b = *d;
b.f();

calling virtual function through pointer to const base class

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.

Calling constant function from another class object

Here is the code that i have
class A
{
public:
void Func1() const;
};
class B
{
public:
A* a;
void Func2() const
{
// do something with 'a'
}
};
void A::Func1() const
{
B b;
b.a = this;
b.Func2();
}
Now obviously this is giving me an error in the line, because I'm trying to convert from const to non-const.
b.a = this;
Is there any way to call Func2 without having to cast away the constness of this. Since Func2 is a const function anyways, it will not change this.
You have to declare the A* as const:
class A
{
public:
void Func1() const;
};
class B
{
public:
const A* a;
void Func2() const
{
// do something with 'a'
}
};
void A::Func1() const
{
B b;
b.a = this;
b.Func2();
}
Since in A::Func1, the this pointer is const.
If class B is always going to work with *a as a const object then as others have said all it takes is to simply change the declaration to
public: const A* a
At this point I should mention that the constness of B::Func2 is a red herring because it has absolutely no relation to the constness of B::a. That B::Func2 is const means that it's not allowed to change the value of a; however, it is allowed to dereference a and mutate the resulting object.
Now, if class B has both const and non-const operations with respect to *a then your class design needs to change. It would be much better if you switched class B to use a const A* a as above, and added another class D : public B that encapsulates all the mutating operations. In addition, a should be hidden behind a property setter; this allows you to do things like
class B {
const A* a;
public:
void setA(const A* a) { this->a = a; }
void Func2() const {}
};
class D : public B {
A* a;
public:
using B::setA;
void setA(A* a) {
this->a = a;
static_cast<B*>(this)->setA(const_cast<const A*>(a));
}
void Func3() { /* do something to D::a */ }
};
With this scheme both B and D keep independent, suitably typed pointers to the object to be accessed. If setA is called on a B, or on a D with a const A* parameter then only B::a is set. If setA is called on a D with an A*, then both B::a and D::a are properly set. This has become possible because by abstracting the member behind a setter you can then overload the setter on the constness of its parameter.
Func2 may not change this, but b.a is not const and you're free to change it afterwards. There's no right way to do this, although workarounds exist, such as mutable or const_cast.
It's the sign of a faulty design.
Yes, make the A * constant:
class B {
public:
const A *a
...
};