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!
}