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.
Related
The code is below. I have one single function f(), and one single function f(int) in my class D, so why is that call ambiguous if both functions have different parameters?
struct A {
void f() {}
};
struct B: virtual A {
void f(int i) {}
};
struct C: virtual A {
void f() {}
};
struct D: B, C {
};
int main()
{
D d;
d.f(5); //ambiguous
}
The problem here is with member name lookup, which occurs before evaluating which functions are viable and applying overload resolution. When name lookup finds names from two or more unrelated base class, this is considered an ambiguous lookup, which is immediately invalid.
For more details, read about unqualified name lookup in class member definitions. (This is not actually the context you have here, but the same rules apply for a member access expression.)
You could work around this by specifying the base class where you want lookup to start, using a qualified-id:
d.B::f(5);
Or, you could make both functions explicitly visible directly in D:
struct D: B, C {
using B::f;
using C::f;
};
The call is ambiguous because both bases A and C of struct D have a function called void f(), even if A's f() is hidden. To disambiguate, you should declare f() in D.
struct A {
void f() {}
};
struct B: virtual A {
void f(int i) {} // < hides A::f(), even if signature is different,
};
struct C: virtual A {
void f() {} // < hides A::f()
};
struct D: B, C {
// f(...) functions in both base... which one hides the other?
};
I got surprised that a public(or private) virtual function can be overridden by a private(or public) virtual function. See below,
class C{
public:
virtual void f(){cout<<"C"<<endl;}
void g(){f();}
};
class D:public C{
private:
virtual void f(){cout<<"D"<<endl;}
};
int main(){
C * c = new D;
c->g();
return 0;
}
the code outputs D. I thought virtual function can only be overridden in the derived class with the same access specifier as in the base class, but this is apparently not how the above code works, am I observing something wrong? Why the access specifiers (public, protected and private) don't put restrictions on how the virtual function is overridden?
The C++11 Standard does not mention anything about the access specifiers of virtual functions in 10.3 Virtual Functions, at least I cannot find any.
Given that, D::f() is not directly accessible through an object or pointer of type D.
D* dPtr = new D;
dPtr->f(); // Does not work.
The following works:
C* cPtr = new D;
cPtr->f();
since C::f() is a public member function of C.
Update
Upon further investigation, I found the following:
11.5 Access to virtual functions
1 The access rules (Clause 11) for a virtual function are determined by its declaration and are not affected by the rules for a function that later overrides it. [ Example:
class B {
public:
virtual int f();
};
class D : public B {
private:
int f();
};
void f() {
D d;
B* pb = &d;
D* pd = &d;
pb->f(); // OK: B::f() is public,
// D::f() is invoked
pd->f(); // error: D::f() is private
}
— end example ]
In the example below, why is B::f() called even though it is private?
I know this fact that :
Access is checked at the call point using the type of the expression used to denote the object for which the member function is called.
#include <iostream>
class A {
public:
virtual void f() { std::cout << "virtual_function"; }
};
class B : public A {
private:
void f() { std::cout << "private_function"; }
};
void C(A &g) { g.f(); }
int main() {
B b;
C(b);
}
Because the standard says so:
[C++11: 11.5/1]: The access rules (Clause 11) for a virtual function are determined by its declaration and are not affected by the rules for a function that later overrides it. [ Example:
class B {
public:
virtual int f();
};
class D : public B {
private:
int f();
};
void f() {
D d;
B* pb = &d;
D* pd = &d;
pb->f(); // OK: B::f() is public,
// D::f() is invoked
pd->f(); // error: D::f() is private
}
—end example ]
The example is the same as yours, lol.
private functions can override public virtual functions from a base class. Accessability is, in a fact, completely ignored when determining whether a function overrides another, so even in
// Redundant private for clarity:
class A { private: virtual void foo(); };
class B : A { public: void foo(); };
B::foo overrides A::foo.
During compile time C++ compiler verifies accessibility of functions and methods based on their type. In function C variable g is of type A (checked during compilation of the code), in which method f is declared as public.
Take a look at this link
In the code below, I get the following warning and error:
test.cpp:15: warning: direct base 'B' inaccessible in 'D' due to ambiguity
test.cpp:15: error: no unique final overrider for 'virtual void A::f()' in 'D'
But if I remove the virtual inheritance of B from A (i.e. struct B : public A), I only get the warning, no error.
struct A
{
virtual void f() = 0;
};
struct B : public virtual A
{
void f() {}
};
class C : public B
{};
struct D : public C, virtual B
{};
int main()
{
return 0;
}
Why? Is this the dreaded diamond?
It's because C inherits in a non-virtual way from B while D inherits in a virtual way from B. This gives you B two times including two f().
Try virtual inheritance of B in C.
Update: So why does it work when you remove the virtual inheritance in B from A? Because it changes the "final overrider". Without virtual in B from A and in C from B you have A two times: once in C (with the final override of f() in B) and once in the virtual B in D (with the final override of f() in B). If you add back the virtual inheritance in B to A, A will be present only once and there will be two final overrides competing to implement the pure f() from A, both in B, once from C and once from the virtual B.
As a workaround you could add a using to D, that is using C::f; or using B::f.
See C++ 10.3/2
Let's look at the definition of 'final overrider' from 10.3[class.virtual]/2
A virtual member function C::vf of a class object S is a final overrider unless the most derived class of which S is a base class subobject (if any) declares or inherits another member function that overrides vf.
In a derived class, if a virtual member function of a base class subobject has more than one final overrider the program is ill-formed.
With virtual inheritance from A, there is only one base class subobject of type A, and its virtual member function f() has more than one final overrider (one in each subobject of type B)
Without virtual inheritance from A, there are two different base class subobjects of type A, and their virtual member functions f() each has its own final overrider (one in each B subobject)
Virtual base sub-objects are 'shared' between all base sub-objects in a complete object. Since A is is shared between D::C::B and D::B it can't tell which B object should have its f() called as the override for A::f().
Consider:
#include <iostream>
struct A {
virtual void f() = 0;
virtual ~A() {}
};
struct B : virtual A
{
void f() { std::cout << "B\n"; }
};
struct C : virtual A
{
void f() { std::cout << "C\n"; }
};
struct D : C, B {};
int main() {
D d;
A *a = dynamic_cast<A*>(&d); // single shared A between B and C
a->f(); // Should B::f() be called, or C::f()?
}
The B and C base sub-objects in D both share the same A base sub-object. When we call A::f() a virtual look-up is done for the overriding function. But both B and C are trying to override it, so which one 'wins'? Does x->f() print "B" or "C"? The answer is that a program that gets into the situation is ill-formed.
When we eliminate the sharing by making B and C inherit non-virtually, then the separate A base sub-objects each have their functions overridden by unique base classes:
#include <iostream>
struct A {
virtual void f() = 0;
virtual ~A() {}
};
struct B : A
{
void f() { std::cout << "B\n"; }
};
struct C : A
{
void f() { std::cout << "C\n"; }
};
struct D : C, B {};
int main() {
D d;
// two different A objects
A *a1 = static_cast<A*>(static_cast<B*>(&d));
A *a2 = static_cast<A*>(static_cast<C*>(&d));
a1->f();
a2->f();
}
In a derived class If I redefine/overload a function name from a Base class,
then those overloaded functions are not accessable/visible to derived class.
Why is this??
If we don't overload the oveloaded function from the base class in derived class
then all the overloaded versions of that function are available to derived class
objects, why is this??
what is the reason behind this. If you explain this in compiler and linker level
that will be more helpful to me. is it not possible to support this kind of scinario??
Edited
For examble:
class B
{
public:
int f() {}
int f(string s) {}
};
class D : public B
{
public:
int f(int) {}
};
int main()
{
D d;
d.f(1);
//d.f(string); //hidden for D
}
Now object 'd' can't access f() and f(string).
TTBOMK this doesn't have a real technical reason, it's just that Stroustrup, when creating the language, considered this to be the better default. (In this it's similar to the rule that rvalues do not implicitly bind to non-const references.)
You can easily work around it be explicitly bringing base class versions into the derived class' scope:
class base {
public:
void f(int);
void g(int);
};
class derived : public base {
public:
using base::f;
void f(float);
void g(float); // hides base::g
};
or by calling the explicitly:
derived d;
d.base::g(42); // explicitly call base class version
The functions are available, you just need to call them explicitly:
struct A {
void f(){}
};
struct B : public A {
void f() {}
};
int main() {
B b;
b.f(); // call derived function
b.A::f(); // call base function
}