I want to bind() to my base class's version of a function from the derived class. The function is marked protected in the base. When I do so, the code compiles happily in Clang (Apple LLVM Compiler 4.1) but gives an error in both g++ 4.7.2 and in Visual Studio 2010. The error is along the lines of: "'Base::foo' : cannot access protected member."
The implication is that the context for the reference is actually within bind(), where of course the function is seen as protected. But shouldn't bind() inherit the context of the calling function--in this case, Derived::foo()--and therefore see the base method as accessible?
The following program illustrates the issue.
struct Base
{
protected: virtual void foo() {}
};
struct Derived : public Base
{
protected:
virtual void foo() override
{
Base::foo(); // Legal
auto fn = std::bind( &Derived::foo,
std::placeholders::_1 ); // Legal but unwanted.
fn( this );
auto fn2 = std::bind( &Base::foo,
std::placeholders::_1 ); // ILLEGAL in G++ 4.7.2 and VS2010.
fn2( this );
}
};
Why the discrepancy in behavior? Which is correct? What workaround is available for the error-giving compilers?
Answer: see boost::bind with protected members & context which quotes this part of the Standard
An additional access check beyond those described earlier in clause 11
is applied when a non-static data member or nonstatic member function
is a protected member of its naming class (11.2)105) 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 name 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.
Workaround: make foo a public member function
#include <functional>
struct Base
{
public: virtual void foo() {}
};
This has nothing to do with bind. Because of the piece of the Standard #rhalbersma already quoted, the expression &Base::foo is illegal in a non-friended member of Derived, in every context.
But if your intent was to do something equivalent to calling Base::foo();, you have a bigger issue: pointers to member functions always invoke a virtual override.
#include <iostream>
class B {
public:
virtual void f() { std::cout << "B::f" << std::endl; }
};
class D : public B {
public:
virtual void f() { std::cout << "D::f" << std::endl; }
};
int main() {
D d;
d.B::f(); // Prints B::f
void (B::*ptr)() = &B::f;
(d.*ptr)(); // Prints D::f!
}
Related
Consider following code:
class A {
public:
virtual void f() const = 0;
void callf() const { f(); }
};
class B : public A {
virtual void f() const { }
};
int main()
{
B x;
x.callf();
return 0;
}
Class B derives from the abstract base class A, but "hides" the implemented method f() as private member.
Still, the inherited member callf() is able to call f(), which was public in the base class.
The code compiles with no warning on g++ 10.1.0 and clang++ 11.1.0.
Is this a legal code, i.e., does the inherited callf() correctly see the private member f()?
Alternatively, would it be possible for the derived class B to implement the purely virtual methods of the base class, such that they can only be called by B (and friends)?
Is this a legal code, i.e., does the inherited callf() correctly see the private member f()?
Yes, it is legal code. From the compiler's point-of-view, the callf function is referencing the f function of its own class; the fact that that is a virtual function does not affect the scope (or accessibility) – only the implementation, which will be in the form of a call to a v-table entry. That v-table entry will be correctly interpreted at run time, when it is called via the derived class.
I'm sure that someone has asked this question before but I simply don't know what to search for. So I'm happy to remove this question as soon as someone points me to a similar one. I'm also happy to rename the questions if someone has a better suggestion :-)
I want to know if the following code is defined behavior by the standard or if this might be compiler/platform dependent:
struct A
{
virtual void f()
{
std::cout << "A::f()" << std::endl;
}
};
struct B : public A
{
// f() is not implemented here
};
struct C : public B
{
virtual void f() override
{
B::f(); // call f() of direct base class although it is not implemented there
std::cout << "C::f()" << std::endl;
}
};
int main()
{
A* pA = new C();
pA->f();
}
The output with Visual Studio 2017 and gcc 5.4.0 is:
A::f()
C::f()
Is it true that the compiler will search upwards in the hierarchy until it finds an implementation? Can you link to the C++ standard? I've tested it by making f() in A pure virtual and the linker nicely tells me that there is an unresolved symbol. Can I rely on that?
As I understand it using the scope operator like B::f() always calls the non-virtual version. So there is no polymorphism happening ever, is it?
Edit: The print statements where misleading, replaced "B::f()" with "C::f()".
The dynamic type of the pointer
A* pA = new C();
is C *.
So the virtual function in the class C is called
struct C : public B
{
virtual void f() override
{
B::f(); // call f() of direct base class although it is not implemented there
std::cout << "B::f()" << std::endl;
}
};
The class B does not redefine the virtual function of the base class A. So in this statement
B::f(); // call f() of direct base class although it is not implemented there
the virtual function defined in the class A is called. That is the table of pointers to virtual functions of the class B contains the address of the function defined in the class A.
In this call
B::f();
there is access to the table of virtual functions of the class B and this table contains the address of the function definition in the class A because the function was not overriden in the class B.
From the C++ STandard (5.2.2 Function call)
...If the selected function is non-virtual, or if the id-expression in the class member access expression is a qualified-id, that function
is called. Otherwise, its final overrider (10.3) in the dynamic type
of the object expression is called; such a call is referred to as a
virtual function call. [
C++ Standard 12.7/4 says:
When a virtual function is called directly or indirectly from a constructor or from a destructor, including during the construction or destruction of the class's non-static data members, and the object to which the call applies is the object (call it x) under construction or destruction, the function called is the final overrider in the constructor's or destructor's class and not one overriding it in a more-derived class. If the virtual function call uses an explicit class member access (5.2.5) and the object expression refers to the complete object of x or one of that object's base class subobjects but not x or one of its base class subobjects, the behavior is undefined.
This text is the same in all versions I checked (though in C++03 it was paragraph 12.7/3).
My question is about the phrase "uses an explicit class member access". Possibly the point of that phrase is to point out that in the constructor/destructor body, virtual calls that use the implicit this-> are safe since the object expression does refer to the object x:
struct A;
A* p;
struct A {
A() { p = this; }
virtual ~A() { if (p == this) p = nullptr; }
virtual void f() {}
};
struct B {
B();
virtual ~B();
virtual void g() {}
};
struct C : public A, public B {
virtual void f() {}
virtual void g() {}
};
B::B() {
if (p) p->f(); // UB if `p` and `this` point at same complete object
g(); // Definitely safe, calls B::g().
}
B::~B() {
if (p) p->f(); // UB if `p` and `this` point at same complete object
g(); // Definitely safe, calls B::g().
}
int main() {
C c; // UB in B::B() and B::~B()!
}
But what if the virtual function call is not syntactically in the definition of the constructor or destructor, but is called indirectly? What is the behavior of this program?
#include <iostream>
struct A {
virtual void f() { std::cout << "A::f()\n"; }
void h() { f(); }
};
struct B {
explicit B(A& a) { a.h(); }
};
struct C : public A, public B {
C() : A(), B(static_cast<A&>(*this)) {}
virtual void f() { std::cout << "C::f()\n"; }
};
int main() {
C c;
}
I would expect that in B::B(A&), calling a.h() is just as undefined as calling a.f(). But we can't say the last sentence in 12.7/4 applies, since the virtual function call does not use an explicit class member access. Have I missed something? Are a.f() and a.h() really supposed to act differently in this context? Is there a Defect Report related to this? Should there be?
9.3.1/3 (in N3485) says
When an id-expression (5.1) that is not part of a class member access syntax (5.2.5) and not used to form
a pointer to member (5.3.1) is used in a member of class X in a context where this can be used (5.1.1),
if name lookup (3.4) resolves the name in the id-expression to a non-static non-type member of some class
C, and if either the id-expression is potentially evaluated or C is X or a base class of X, the id-expression is
transformed into a class member access expression (5.2.5) using (*this) (9.3.2) as the postfix-expression to
the left of the . operator.
In your second example, this means the body of A::h() gets transformed into (*this).f(), making the call an explicit class member access. Thus the last line of 12.7/4 applies; the behavior is undefined.
It should not make any difference.
The standard says:
When a virtual function is called directly or indirectly
However, your compiler may well have a bug - perhaps because it optimises the code in h, thinking that it understands what is going on (and not actually doing the right thing). You haven't mentioned WHICH compiler you are using, so it's not possible to say if there is a defect report...
Edit: Both g++ 4.8.2 and clang++ 3.5 prior to release from a few weeks back (with -std=c++11, in case that makes a difference) calls C::f() in the destructor, and A::f() in the constructor for your first testcase. In the second test-case, g++ calls A::f(), where clang++ calls C::f(). So clearly, the compiler seems to do "whatever it feels like" here. [Note that since it's "undefined", it can do all sorts of different things, including "what you expect"].
(In the first test-case, I modified p to be a to make it compile, and added printouts in f and g functions)
It seems that a protected member from a template policy class is inaccessible, even with a class hierarchy which seems correct.
For instance, with the following code snippet :
#include <iostream>
using namespace std;
template <class T>
class A {
protected:
T value;
T getValue() { return value; }
public:
A(T value) { this->value = value; }
};
template <class T, template <class U> class A>
class B : protected A<T> {
public:
B() : A<T>(0) { /* Fake value */ }
void print(A<T>& input) {
cout << input.getValue() << endl;
}
};
int main(int argc, char *argv[]) {
B<int, A> b;
A<int> a(42);
b.print(a);
}
The compiler (clang on OS X, but gcc returns the same type of error) returns the following error :
Untitled.cpp:18:21: error: 'getValue' is a protected member of 'A<int>'
cout << input.getValue() << endl;
^
Untitled.cpp:25:5: note: in instantiation of member function 'B<int, A>::print' requested here
b.print(a);
^
Untitled.cpp:8:7: note: can only access this member on an object of type 'B<int, A>'
T getValue() { return value; }
^
1 error generated.
The strange thing is that the last note from the compiler is totally correct but already applied since the b object is of type 'B<int, A>'. Is that a compiler bug or is there a problem in the code ?
Thanks
You have misunderstood the meaning of protected access.
Protected members are callable by derived classes. But only on the base object contained inside the class itself.
For example, if i simplify the problem, using :
class A {
protected:
void getValue(){}
};
class B : protected A
{
public:
void print(A& input)
{
input.getValue(); //Invallid
}
};
getValue cannot be called on a "A" object other than the "A" object inside the class itself.
This for example is valid.
void print()
{
getValue(); //Valid, calling the base class getValue()
}
As pointed out by Dan Nissenbaum and shakurov. This is however also valid:
void print(B& input)
{
input.getValue();
}
This is because we explicitly say that input is a object of B. And the compiler know that all that objects of B has protected access to getValue. In the case when we pass a A&, the object might as-well be a type of C, wich could be derrived from A with private access.
Let's forget for a minute about the template and look at this:
class A {
protected:
int value;
int getValue() { return value; }
public:
A(int value) { this->value = value; }
};
class B : protected A {
public:
B() : A(0) { /* Fake value */ }
void print(A& input) {
cout << input.getValue() << endl;
}
};
The print() method's implementation is wrong because you can't access non-public member of A inside B. And here's why: from within B, you can only access non-public members of B. Those members may be either inherited or not — it doesn't matter.
On the other hand, A& input may not be a reference to an instance of B. It may be a reference to another subclass (which may well have getValue() inaccessible).
Member functions of a derived class have access to protected base class members within any object of its type that is passed as an argument so long as the explicitly declared class of the object passed as an argument is that of the the derived class (or a further derived class).
Objects explicitly passed as the base class type cannot have their protected members accessed within the derived class's member functions.
In other words, if we have:
class A
{
protected:
int x;
}
class B : public A
{
void foo(B b)
{
b.x; // allowed because 'b' is explicitly declared as an object of class B
}
void goo(A a)
{
a.x; // error because 'a' is explicitly declared as having *base* class type
}
};
...then the line a.x is not allowed because the explicit type of the argument is A, but the rule for protected access only applies to objects explicitly defined as the same class as the class attempting to access the member. (...Or a class derived from it; i.e., if class Cderives from B, then passing an object explicitly declared as an object of class C will also have x accessible within B member functions.)
The reason for this is given by shakurov when he writes (paraphrasing)
A& input might not be a reference to an instance of B. It may be a
reference to another subclass (which may well have getValue()
inaccessible)
An excellent explication of this answer is also given here: accessing a protected member of a base class in another subclass.
As a matter of interest, I believe that this comes from the C++ standard here:
11.4 Protected member access [class.protected] 1 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)115 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.
Don't get distracted by the template. It has nothing to do with the error. The line in main that the compiler is complaining about creates an object of type B<int, a> and tries to access a protected member. That's not legal, regardless of the type. You can only use protected members from inside a member function or friend function. For example:
struct S {
protected:
void f();
};
int main() {
S s;
s.f(); // error: attempts to call a protected member function
}
I want to bind() to my base class's version of a function from the derived class. The function is marked protected in the base. When I do so, the code compiles happily in Clang (Apple LLVM Compiler 4.1) but gives an error in both g++ 4.7.2 and in Visual Studio 2010. The error is along the lines of: "'Base::foo' : cannot access protected member."
The implication is that the context for the reference is actually within bind(), where of course the function is seen as protected. But shouldn't bind() inherit the context of the calling function--in this case, Derived::foo()--and therefore see the base method as accessible?
The following program illustrates the issue.
struct Base
{
protected: virtual void foo() {}
};
struct Derived : public Base
{
protected:
virtual void foo() override
{
Base::foo(); // Legal
auto fn = std::bind( &Derived::foo,
std::placeholders::_1 ); // Legal but unwanted.
fn( this );
auto fn2 = std::bind( &Base::foo,
std::placeholders::_1 ); // ILLEGAL in G++ 4.7.2 and VS2010.
fn2( this );
}
};
Why the discrepancy in behavior? Which is correct? What workaround is available for the error-giving compilers?
Answer: see boost::bind with protected members & context which quotes this part of the Standard
An additional access check beyond those described earlier in clause 11
is applied when a non-static data member or nonstatic member function
is a protected member of its naming class (11.2)105) 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 name 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.
Workaround: make foo a public member function
#include <functional>
struct Base
{
public: virtual void foo() {}
};
This has nothing to do with bind. Because of the piece of the Standard #rhalbersma already quoted, the expression &Base::foo is illegal in a non-friended member of Derived, in every context.
But if your intent was to do something equivalent to calling Base::foo();, you have a bigger issue: pointers to member functions always invoke a virtual override.
#include <iostream>
class B {
public:
virtual void f() { std::cout << "B::f" << std::endl; }
};
class D : public B {
public:
virtual void f() { std::cout << "D::f" << std::endl; }
};
int main() {
D d;
d.B::f(); // Prints B::f
void (B::*ptr)() = &B::f;
(d.*ptr)(); // Prints D::f!
}