Boost :: bind with protected members and context

In the code below, there are two "equivalent" calls to std::for_each using boost:bind expressions. The indicated line is compiled, the indicated faulty line is out of order. The best explanation I can find in the standard is "because we said so." I am looking for "why the standard indicates this behavior." My assumptions are below.

My question is simple: why does the specified line compile and the equivalent next line does not compile (and I don’t want it because "the standard says so", I already know that - I will not accept any answers that give this as an explanation, I would like explain why the standard says so).

Notes. Although I use boost, boost does not approach this question, and the error in various formats was reproduced using g ++ 4.1. * and VC7.1.

 #include <boost/bind.hpp> #include <iostream> #include <map> #include <algorithm> class Base { protected: void foo(int i) { std::cout << "Base: " << i << std::endl; } }; struct Derived : public Base { Derived() { data[0] = 5; data[1] = 6; data[2] = 7; } void test() { // Compiles std::for_each(data.begin(), data.end(), boost::bind(&Derived::foo, this, boost::bind(&std::map<int, int>::value_type::second, _1))); // Fails to compile - why? std::for_each(data.begin(), data.end(), boost::bind(&Base::foo, this, boost::bind(&std::map<int, int>::value_type::second, _1))); } std::map<int, int> data; }; int main(int, const char**) { Derived().test(); return 0; } 

The specified line does not work with this error: main.C: In the member function 'void Derived :: test ()': main.C: 9: error: 'void Base :: foo (int)' is protected by main.C: 31 : error: in this context

As noted, the supposedly equivalent statement above compiles cleanly (and if the offender’s comment is commented out, it starts with the expected print result of β€œ5”, β€œ6”, β€œ7” on separate lines).

When searching for explanations, I came across 11.5.1 in the standard (in particular, I watched the draft 2006-11-06):

An additional access check beyond those described in clause 11 above applies when a non-static data member or non-static member function is a protected member of its naming class (11.2) 105) As described above, access to the protected member is granted because the link is in another or a member of any class C. If access consists in forming a pointer to a member (5.3.1), the nested qualifier must denote C or a class derived from C. All other access assumes (possibly implicit) (5.2.5). In this case, the class of the expression object must be C or a class derived from C.

After reading this, it became obvious why the second statement failed, while the first succeeded, but then the question arose: what is the reason for this?

My initial thought was that the compiler extends the boost :: bind patterns to find that Base :: foo was protected and kicked it out because boost :: bind <...> was not a friend. But the more I thought about this explanation, the less it made sense, because if I remember correctly, as soon as you take a pointer to a member (if you are initially in member access control), all access control information is lost (i.e. I could define a function that returns an arbitrary pointer to an element that alternately returns a public, protected or private member depending on some input, and the return will not be wiser).

I thought more about this, and the only plausible explanation that I could come up with why it should matter was in the case of multiple inheritance. In particular, depending on the layout of the class, the member pointer when calculating from Base will be different from that calculated from Derived.

+5
c ++
Jun 16 2018-10-06T00:
source share
3 answers

All about "context." In the first call, the context of the Derived call, which has access to protected Base members and, therefore, can receive addresses from them. In the second context, it is β€œoutside” Derived and, therefore, outside Base , so access to the protected member is not allowed.

+5
Jun 17 '10 at 13:25
source share

In fact, this seems logical. Inheritance gives you access to Derived :: foo, not Base :: foo. Let me illustrate a sample code:

 struct Derived : public Base { void callPrivateMethod(Base &b) { // this should obviously fail b.foo(5); // pointer-to-member call should also fail void (Base::*pBaseFoo) (int) = &Base::foo; // the same error as yours here (b.*pBaseFoo)(5); } }; 
+1
Sep 24 '14 at 15:23
source share

The reason for this limitation is to provide access control between different classes that have a common base.

This is supported by the notes in the defect of the defect report of the main language # 385 , the corresponding part copied here for reference:

[...] the reason why we have this rule is that the use of inherited protected members of C may differ from their use in a class of sisters, for example D Thus, members and friends of C can only use B::p according to the use of C , i.e. In objects C or derivatives of C

As an example of something, this rule prevents:

 class B { protected: void p() { }; }; class C : public B { public: typedef void (B::*fn_t)(); fn_t get_p() { return &B::p; // compilation error here, B::p is protected } }; class D : public B { }; int main() { C c; C::fn_t pbp = c.get_p(); B * pb = new D(); (pb->*pbp)(); } 

The protected status of D::p is that we want the compiler to perform enforcement actions, but if the compiled above is not the case.

0
Dec 28 '17 at 21:14
source share



All Articles