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.