13

bind()派生クラスから関数の基本クラスのバージョンを取得したいと考えています。関数は、ベースで保護されているとマークされています。私がそうすると、コードは Clang (Apple LLVM Compiler 4.1) で問題なくコンパイルされますが、g++ 4.7.2 と Visual Studio 2010 の両方でエラーが発生します。エラーは次の行に沿っています: "'Base::foo' : cannot保護されたメンバーにアクセスしてください。」

つまり、参照のコンテキストは実際には 内bind()にあり、もちろん関数は保護されていると見なされます。しかしbind()、呼び出し元の関数のコンテキスト (この場合は) を継承してDerived::foo()、基本メソッドがアクセス可能であると見なすべきではありませんか?

次のプログラムは、この問題を示しています。

struct Base
{
protected: virtual void foo() {}
};

struct Derived : public Base
{
protected:
    virtual void foo() override
    {
        Base::foo();                        // Legal

        auto fn = std::bind( &Derived::foo, 
            std::placeholders::_1 );        // Legal but unwanted.
        fn( this );

        auto fn2 = std::bind( &Base::foo, 
            std::placeholders::_1 );        // ILLEGAL in G++ 4.7.2 and VS2010.
        fn2( this );
    }
};

行動の不一致はなぜですか?どちらが正しい?エラーが発生するコンパイラにはどのような回避策がありますか?

4

2 に答える 2

11

回答:標準のこの部分を引用している保護されたメンバーとコンテキストを持つboost::bindを参照してください

非静的データメンバーまたは非静的メンバー関数がその命名クラスの保護されたメンバーである場合、節11で前述したものを超える追加のアクセスチェックが適用されます(11.2)105) 前述のように、保護されたメンバーへのアクセスは、アクセスがメンバーへのポインターを形成することである場合 (5.3.1)、nested-name-specifier は C または C から派生したクラスを指定する必要があります。他のすべてのアクセスには (おそらく暗黙の)オブジェクト式(5.2.5)。この場合、オブジェクト式のクラスは C または C から派生したクラスでなければなりません。

回避策:メンバー関数を作成fooしますpublic

#include <functional>

struct Base
{
public: virtual void foo() {}
};
于 2013-01-24T19:55:08.347 に答える
10

これはとは何の関係もありませんbind。すでに引用されている標準@rhalbersmaの一部のため、すべてのコンテキストで、&Base::fooの非フレンドメンバーでの表現は違法です。Derived

ただし、を呼び出すのと同等のことを行うことを意図しているBase::foo();場合は、より大きな問題があります。メンバー関数へのポインターは、常に仮想オーバーライドを呼び出します。

#include <iostream>

class B {
public:
    virtual void f() { std::cout << "B::f" << std::endl; }
};

class D : public B {
public:
    virtual void f() { std::cout << "D::f" << std::endl; }
};

int main() {
    D d;
    d.B::f();   // Prints B::f

    void (B::*ptr)() = &B::f;
    (d.*ptr)(); // Prints D::f!
}
于 2013-01-24T20:27:34.810 に答える