代码之家  ›  专栏  ›  技术社区  ›  Nathan Ernst

boost::绑定受保护的成员和上下文

c++
  •  6
  • Nathan Ernst  · 技术社区  · 16 年前

    在下面的代码中,有两个“等价”调用 std::for_each 使用 boost:bind 表达。指示行编译,指示的失败行失败。我能在标准中找到的最好的解释是“因为我们说过”。我在找“为什么标准会指出这种行为”。我的假设如下。

    为什么? 标准是这样说的)。

    #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;
    }
    

    指示的线路出现故障,出现以下错误: main.C:在成员函数“void-Derived::test()”中: main.C:9:错误:“void Base::foo(int)”受保护 main.C:31:错误:在此上下文中

    如前所述,上面假定的等价语句编译得很干净(如果有问题的语句被注释掉,则在单独的行上打印5、6、7的预期结果)。

    额外的访问检查 当非静态数据 其命名类的受保护成员 (11.2)105)如前所述, 无法访问受保护的成员 因为引用发生 在某个C类的朋友或成员身上。 如果访问要形成指向 嵌套名称说明符应命名为C或 访问涉及一个(可能是隐式的) 对象表达式(5.2.5)。在这个 case,对象的类 表达式应为C或a级 源于C。

    读了这篇文章之后,很明显,为什么第二个陈述失败了,而第一个陈述成功了,但接着问题来了:这其中的理由是什么?

    我最初的想法是编译器正在扩展boost::bind模板,发现Base::foo受到保护,并将其踢出,因为boost::bind<&燃气轮机;他不是朋友。但是,我越是考虑这个解释,它就越没有意义,因为如果我没记错的话,只要您将指针指向某个成员(假设您最初处于该成员的访问控制范围内),所有访问控制信息都将丢失(即,我可以定义一个函数,该函数返回指向某个成员的任意指针,该成员根据某些输入交替返回公共、受保护或私有成员,而返回程序将不再明智)。

    3 回复  |  直到 16 年前
        1
  •  7
  •   GrafikRobot    16 年前

    一切都是关于“语境”。在第一个调用中,调用的上下文是 Derived Base 因此被允许记录他们的地址。在第二种情况下,上下文是“外部的” 派生 因此,在 底座 因此不允许受保护的成员访问。

        2
  •  1
  •   itotsev    11 年前

    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);
        }
    };
    
        3
  •  0
  •   Chris Hunt    8 年前

    这一点在中的注释中得到了加强 Core Language Defects Report defect #385

    […]我们有这个规则的原因是 C 例如,继承的受保护成员的使用可能不同于它们在兄弟类中的使用 D . 因此 C 只能使用 B::p 以符合 C 的用法,即 或源自- C

    作为这条规则所阻止的事情的一个例子:

    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)();
    }
    

    的受保护状态 D::p 是我们希望编译器强制执行的东西,但是如果上面编译的话就不是这样了。