Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
213 views
in Technique[技术] by (71.8m points)

c++ - boost::bind with protected members & context

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

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

It's all about "context". In the first call the context of the call is Derived which has access to the protected members of Base and hence is allowed to take addresses of them. In the second the context is "outside of" Derived and hence outside of Base so the protected member access is not allowed.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...