I recently ran into a problem when using a private inheritance scheme in which the base class defined a template method and the (privately) derived class made that method public via a using declaration under the public access specifier. The template was designed to take the address of a function and invoke that function pointer. However, upon attempting to pass the name of the function to the derived class template method, I receive an error message that states that the base-class method cannot access a private member declared in the derived class. Here is a code segment that models the issue:
class A
{
public:
template<class T> void Funct(T pFunct) { }
};
class B : private A
{
public:
using A::Funct;
};
void Show(void) { }
int main(void)
{
B b;
b.Funct(Show);
}
The exact resulting error message is: 'A::Funct' : cannot access private member declared in class 'B'
I am able to resolve the issue simply by:
1)Preceding the function argument name with the address-of operator:
b.Funct(&Show);
2)Explicitly qualifying the template type argument:
b.Funct<void(*)(void)>(Show)
If the Show() function were a template as well, I would need to explicitly qualify the template using the proper template type arguments used to instantiate Show.
My question is not how to solve the problem but why the error message is being generated. Why does instantiating Funct() with Show versus with &Show cause the compiler to do two different things. And why does instantiating Funct() with Show cause the Funct() method to attempt to access the private data in class B (which I'm assuming is the A subobject in class B)?
Compiles fine with Comeau Online. Ergo, compiler bug. Report it.
Cheers & hth.,
bcc32 gives error: error bccE2247: 'Funct<void (*)()>(void (*)())' is not accessible in function main()
I believe this is a compiler bug.
Related
Found this strange compilation behavior, checked on VS2012, VS2017 and https://www.onlinegdb.com/online_c++_compiler)
Basically for private nested classes you can call public functions outside, but not public constructors.
3 questions:
what is the reasoning behind compiler letting me call func()?
if compiler lets me call func(), why I cannot call ctor?
if I cannot call ctor, how come emplace_back is able to do it?
class Outer {
struct PrivateInner {
PrivateInner() {}
void func() {}
};
public:
PrivateInner inner;
std::vector<PrivateInner> innerVect;
};
void f1()
{
Outer c;
c.inner.func(); // COMPILING, but why?
}
void f2()
{
Outer c;
c.innerVect.push_back(Outer::PrivateInner()); // NOT COMPILING, why no access to ctor if there is access to func()?
c.innerVect.emplace_back(); // COMPILING, but why? Doesn't it call Outer::PrivateInner inside?
}
As I see I still can create a (static) function createObject():
class Outer {
struct PrivateInner {
PrivateInner() {}
static PrivateInner createObject() { return PrivateInner(); }
void func() {}
};
.....
};
and then call it.
createObject() may be non-static if calling static from instances is not pure standard thing.
c.innerVect.push_back(c.inner.createObject()); // COMPILING
to "hack" compilation
Note that the nested struct PrivateInner is declared as private, so only Outer::PrivateInner is private, you can't use this name to declare variable like Outer::PrivateInner pi; without sufficient access right, but you can write it like decltype(Outer::inner) pi;.
On the other hand, its constructor and member function are public, so they can be called.
c.inner.func(); // COMPILING, but why?
func() is public, so if you have got an instance of type Outer::PrivateInner you can call func on it.
c.innerVect.push_back(Outer::PrivateInner()); // NOT COMPILING, why no access to ctor if there is access to func()?
It has nothing to do with the constructor, you just can't use the name Outer::PrivateInner here.
c.innerVect.emplace_back(); // COMPILING, but why? Doesn't it call Outer::PrivateInner inside?
The constructor is public, then it could be used to construct the object. std::vector doesn't use the name like Outer::PrivateInner directly; it uses the name specified as the template argument.
BTW: For the same reason,
c.innerVect.push_back(c.inner.createObject()); // COMPILING
but c.innerVect.push_back(Outer::PrivateInner::createObject()); won't compile.
The member is public so of course you can call its member functions. If its name is visible, then so are its own public members.
Likewise the inner class is private, so of course you can't refer to its name from outside the class. These are the basic definitions of the access control rules.
emplace_back can call the constructor because std::vector received the type as a template parameter from someone who was allowed to refer to it. The access check occurred when the template was instantiated. At that point its name was accessible.
You can call the constructor outside of the class anywhere using decltype:
Outer c;
auto this_works_too = decltype(c.inner){};
This works because you don't have to refer to it by an inaccessible name.
The access control is applied to names. That means only the name of the struct PrivateInner is restricted. The members of the struct itself have own access control. So all members of PrivateInner are public.
11 Member access control [class.access]
1 A member of a class can be
private; that is, its name can be used only by members and friends of the class in which it is declared.
...
4 Access control is applied uniformly to all names, whether the names
are referred to from declarations or expressions. ...
As you already found out, you can use the constructor (and the struct in general) if you're not using its name:
c.innerVect.push_back({});
c.innerVect.emplace_back();
even
template <typename T>
T createPrivateInner()
{
return T();
}
...
c.innerVect.push_back(createPrivateInner<decltype(Outer::inner)>());
The member "createObject()" is private. So, of course, you cannot access it. You should add some member function in public field to implement this private member.
using namespace std;
#include <cstdio>
#include <iostream>
class One{
private:
virtual void func(){
cout<<"bark!"<<endl;
}
};
class Two: public One{
public:
void func(){
cout<<"two!"<<endl;
}
};
int main(){
One *o = new Two();
o->func();
}
Why is there an error on o->func()?
I don't know the mechanism behind it... In my opinion, o->func() should call the func() in the derived class, which is public, so there wouldn't be problems, but it says:
error: ‘virtual void One::func()’ is private
Accessibility check is performed based on the static type of the object. The type of o is One*. This means that if One::func() is private, then o->func() won't compile.
On the other hand, which virtual member function will be called (i.e. dynamic dispatch) happens at run-time, based on the dynamic type of the object. So if One::func() is public, o->func() will call Two::func(), because o is pointing to an object of type Two.
For your sample code and use case, making One::func() private is just meaningless. But note that there's a famous idiom called Non-Virtual Interface, which makes use of private virtual member functions of base class.
Other suggestions:
Don't forget to delete o;
Add a virtual destructor in the base class One. Otherwise delete o; will lead to undefined behavior; e.g. the destructor of Two might not be invoked.
class One {
public:
virtual ~One() {}
// ...
};
A subclass can't ease inheritance restriction,
even though func is virtual, it is still the inheritance restrictions remain.
please see this answer for compliated view of inheritance restrictions :
Difference between private, public, and protected inheritance
Please check Access specifiers and virtual functions.
From standard :
§11.5 [class.access.virt] 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.
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. The
access of the member function in the class in which it was defined is
in general not known.
If name lookup determines a viable function to be a virtual function, the access specifier of the virtual function is checked in the scope of the static type of the object expression used to name the function. At run time, the actual function to be called could be defined in the derived class with a completely different access specifier. This is because 'access specifiers' are a compile time phenomenon.
Since access specifier of function func() is checked in the scope of One *o, and it is private in class One, it produces error.
If Onedeclares func() as public, and Two declares it private, there won't be any errors. See this Private function invoked and it works. Could any of you reason it please
This question already has answers here:
Why does an overridden function in the derived class hide other overloads of the base class?
(4 answers)
Closed 5 years ago.
Suppose I have the following classes:
class Base
{
public:
virtual void myMethod()
{
}
virtual void myMethod(int x)
{
}
};
class Derived : public Base
{
};
In this situation the following code compiles just fine.
int main(void)
{
Derived obj;
obj.myMethod();
return (0);
}
The problem arises when I try to override one of myMethods like below.
class Derived : public Base
{
public:
virtual void myMethod(int x)
{
}
};
Now the code won't compile and gives the error:
error C2660: 'Derived::myMethod' : function does not take 0 arguments
I have overridden one of the overloaded functions and apparently this has hidden the other one from Derived class. To get rid of the error I have to override all the overloads. This is against my expectation and doesn't seem rational to me. Why does this code behave this way?
The problem can be reproduced here.
Indeed, declaring a function in one scope hides anything with the same name in a wider scope, so your override in the derived class hides the overload in the base class.
This is usually not a problem, since you'd usually interact with polymorphic classes via the base class; and usually what you want, since it prevents changes to the base class from unexpectedly changing the behaviour of code that does interact with the derived class.
But you can easily bring it back into the derived class's scope if you want:
using Base::myMethod;
or refer to the function by qualified name:
obj.Base::myMethod();
your compiler is 100% right.
you overloaded your function to take an integer as argument, then this function hid all of the base class function - so obj calls myMethod(int) be default , yet you don't provide your function an integer.
if you fix your code to be
obj.myMethod(4);
the problem solved.
when overriding a function in the derived class - it hides all the other base class overloads. one can still call the base class with this syntax :
obj.Base::myMethod();
In more in depth answer , this is WHY it happens.
first of all, we need to understand HOW the compiler compiles Object oriented code. remember - the functions compiles into assembly commands and they sit in the code segment. the variables compiles into rows of memory that sit wither in the stack , heap or data segments. functions sits in on part of the application , the variables in a complete different areas. how does the compiler compile a class with variables AND functions? well, it doesn't. the idea goes like this:
let's say a have a class named X with some variables and some functions
1) take all the member functions and bring them out of the class
2) add another argument to each-now-globally-declared functions - const X* this
3) each time you see the syntax x.someFunc(args..) change it to be someFunc(args..,&x)
4) in the functions - if you don't recognize a symbol - try attaching it a this-> and see if you can compile this
5) compile X as C struct and the other as C functions
6)do the same for derived classes
(of course , there is the virtual table issue , but let's stop here)
IN YOUR EXAMPLE:
the psuedo code that might represent the compiler parsed-code is
struct Base{}
struct Derived{}
void myMethod(const Base* this);
void myMethod(int x,const Base* this);
void myMethod(int x, const Derived* this);
//what you tried to do is :
myMethod (&obj);
but the compiler can't find any function that matches these arguments!
this is not very intuitive for someone who don't initially knows how object oriented compiles, but it makes more sense after understanding this compiling procedure.
By overriding the function in Derived class you hide all overloaded implementations of that function name existing in Base class.
Therefore overriding void Base::myMethod(int) with void Derived::myMethod(int) generates code only for void Derived::myMethod(int) and doesn't for void Derived::myMethod().
As mentioned before you can call Base's function explicitly:
obj.Base::myMethod().
I have a template method in a parent class, that should call another method from the base class. It works, if the method is explicitly defined in the base class, but it doesn't work if the method is inherited. I can't figure out what's exactly wrong with this code (although I know it's a little weird :)
class A
{
protected:
virtual void someMethod()
{
}
template <class TBaseClass>
void templateMethod()
{
TBaseClass::someMethod();
}
};
class B : public A
{
};
class C : public B
{
protected:
virtual void someMethod()
{
templateMethod<A>(); // this works
templateMethod<B>(); // this doesn't
}
};
This ends up with compiler error:
error C2352: 'A::someMethod' : illegal call of non-static member function
What exactly is wrong? I'm not looking for workarounds, that's easy. I'd like to know why is this incorrect.
In template <TBaseClass> void A::templateMethod() the invocant, this, is of type A *. So when you try to call B::someMethod on it, the compiler won't recognize it a object method call, because B is not a base class, but it can still be a static method call, so the compiler will try that, find B::someMethod inherited via A and complain it is not static. The fact that A is a base class of this is not relevant; only that B is not.
The issue is that class A is not automatically granted access to class B. The methods within the class don't know that the current instance of A is actually of type B.
To do what you're trying, you need to cast A to the target type:
template <class TBaseClass>
void templateMethod()
{
static_cast<TBaseClass*>(this)->someMethod();
}
This will work if the particular instance of A you're calling really is a B (as it is in the example you give). There is no type checking done here to be sure it is. If you pass a class that is not in the inheritance hierarchy you will be in the work of undefined behavior.
That said, your example has "someMethod()" as virtual. If you're doing what I just did here then making that method virtual may not make sense, as you are explicitly saying which instance you want. If you do leave it virtual, then just calling someMethod() directly in A will get you the most derived instance.
I think it's not working because you are trying to call a static method.
Static methods can be inherited but they can't be virtual.
Thats why A::someMethod will work and B::someMethod won"t work.
I'm confused about the errors generated by the following code.
In Derived::doStuff, I can access Base::output directly by calling it.
Why can't I create a pointer to output() in the same context that I can call output()?
(I thought protected / private governed whether you could use a name in a specific context, but apparently that is incomplete?)
Is my fix of writing callback(this, &Derived::output); instead of callback(this, Base::output) the correct solution?
#include <iostream>
using std::cout; using std::endl;
template <typename T, typename U>
void callback(T obj, U func)
{
((obj)->*(func))();
}
class Base
{
protected:
void output() { cout << "Base::output" << endl; }
};
class Derived : public Base
{
public:
void doStuff()
{
// call it directly:
output();
Base::output();
// create a pointer to it:
// void (Base::*basePointer)() = &Base::output;
// error: 'void Base::output()' is protected within this context
void (Derived::*derivedPointer)() = &Derived::output;
// call a function passing the pointer:
// callback(this, &Base::output);
// error: 'void Base::output()' is protected within this context
callback(this, &Derived::output);
}
};
int main()
{
Derived d;
d.doStuff();
}
Edit: I'd love to know where this is in the stardard, but mostly I'm just trying to wrap my head around the concept. I think my problem is that callback doesn't have access to protected members of Derived, but it is able to call Derived::output if you pass it a pointer. How is a protected member of Derived that comes from Derived different from a protected member of Derived that comes from Base?
In short, it's "because the standard says so." Why? I don't know, I've emailed a couple of the standards guys, but haven't received a response, yet.
Specifically, 11.5.1 (from C++0x FCD):
An additional access check beyond
those described earlier in Clause 11
is applied when a non-static data
member or non-static member function
is a protected member of its naming
class (11.2)114 As described earlier,
access to a protected member is
granted because the reference occurs
in a friend or member of some class C.
If the access is to form a pointer to
member (5.3.1), the
nested-name-specifier shall denote C
or a class derived from C. All other
accesses involve a (possibly implicit)
object expression (5.2.5). In this
case, the class of the object
expression shall be C or a class
derived from C.
Edit:
Also, you'll see that you change the code to the following, according to what the standard specifies, it will compile (and run) cleanly:
void (Base::*derivedPointer)() = &Derived::output;
Edit: I'm not sure if this is a "Where is this in the standard?" question or a "Why is it designed that way?" question, this answers the latter (I don't have a copy of the standard itself to play with)
I believe this is because a function with protected or friend access to base would be able to circumvent access protection by passing the function pointer to methods which should not have access to base's private members.
In this example, callback does not have access to base, and therefore should not be able to call one of it's private functions.