boost::bind with protected members & context
<p>In the below code, there are two "equivalent" calls to <code>std::for_each</code> using <code>boost:bind</code> 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.</p>

<p>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 <em>why</em> the standard says so).</p>

<p>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.</p>

<pre><code>#include &lt;boost/bind.hpp&gt;
#include &lt;iostream&gt;
#include &lt;map&gt;
#include &lt;algorithm&gt;

class Base
{
protected:
        void foo(int i)
        { std::cout &lt;&lt; "Base: " &lt;&lt; i &lt;&lt; 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(&amp;Derived::foo, this,
                                boost::bind(&amp;std::map&lt;int, int&gt;::value_type::second, _1)));

                // Fails to compile - why?
                std::for_each(data.begin(), data.end(),
                        boost::bind(&amp;Base::foo, this,
                                boost::bind(&amp;std::map&lt;int, int&gt;::value_type::second, _1)));
        }

        std::map&lt;int, int&gt; data;
};

int main(int, const char**)
{
        Derived().test();

        return 0;
}
</code></pre>

<p>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</p>

<p>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).</p>

<p>While searching for an explanation, I came across 11.5.1 in the standard (specifically, I’m looking at the 2006-11-06 draft):</p>

<blockquote>
  <p>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.</p>
</blockquote>

<p>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?</p>

<p>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&lt;…> 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).</p>

<p>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.</p>
