In the below code, there are two "equivalent" calls to std::for_each
using boost:bind
expressions. The indicated line compiles, the indicated failing line fails. The best explanation I can find in the standard amounts to "because we said so". I'm looking for "why the standard indicates this behavior". My suppositions are below.
My question is simply: Why does the indicated line compile and the equivalent following line fail to compile (and I don't want because "the standard says so", I already know that - I will not accept any answers that give this as an explanation; I'd like an explanation as to why the standard says so).
Notes: Although I use boost, boost is irrelevant to this question, and the error in various formats has been 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 indicated line fails with this error:
main.C: In member function 'void Derived::test()':
main.C:9: error: 'void Base::foo(int)' is protected
main.C:31: error: within this context
As noted, the supposedly equivalent statement above compiles cleanly (and if the offending statement is commented out, runs with the expected result of printing “5”, “6”, “7” on separate lines).
While searching for an explanation, I came across 11.5.1 in the standard (specifically, I’m looking at the 2006-11-06 draft):
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.
After reading this, it became evidently why the second statement failed while the first succeeded, but then the question came up: What is the rationale for this?
My initial thought was that the compiler was expanding the boost::bind templates, discovering that Base::foo was protected and kicking 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 recall correctly, as soon as you take the pointer to a member (assuming you initially are within access control of the member), all access control information is lost (i.e. I could define a function that returns an arbitrary pointer to a member that alternately returns a public, protected or private member depending on some input and the returner would be none the wiser).
More I thought about it, and the only plausible explanation I could come up with why it should make a difference was in the case of multiple inheritance. Specifically, that depending on the class layout, the member pointer when calculated from Base would be different than that calculated from Derived.
See Question&Answers more detail:
os