Std:: bind() - базовая защищенная функция-член из функции-члена производного класса

Я хочу bind() перейти к моей базовой версии функции из производного класса. Функция отмечена как защищенная в базе. Когда я это делаю, код успешно компилируется в Clang (Apple LLVM Compiler 4.1), но дает ошибку как в g++ 4.7.2, так и в Visual Studio 2010. Ошибка в строках: "Base:: foo": не может доступ к защищенному члену. "

Подразумевается, что контекст ссылки действительно находится в пределах 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 );
    }
};

Почему расхождение в поведении? Что правильно? Какое обходное решение доступно для компиляторов с ошибкой?

Ответ 1

Ответ: см. boost:: bind с защищенными членами и контекстом, который цитирует эту часть Стандартного

Дополнительная проверка доступа за пределы, описанные ранее в разделе 11 применяется, когда нестатический элемент данных или нестатическая функция-член является защищенным членом его класса именования (11.2) 105). Как описано ранее доступ к защищенному члену предоставляется, поскольку ссылка происходит в другом или члене некоторого класса C. Если доступ должен формироваться указатель на член (5.3.1), спецификатор вложенного имени должен называть C или класс, полученный из C. Все другие обращения включают (возможно неявное) выражение объекта (5.2.5). В этом случае класс выражение объекта должно быть C или классом, производным от C.

Обходной путь: make foo a public функция-член

#include <functional>

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

Ответ 2

Это не имеет ничего общего с 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!
}