boost :: bind с защищенными членами и контекстом - PullRequest
6 голосов
/ 17 июня 2010

В приведенном ниже коде есть два "эквивалентных" вызова std::for_each с использованием выражений boost:bind. Указанная строка компилируется, указанная ошибочная строка завершается ошибкой. Лучшее объяснение, которое я могу найти в стандарте, сводится к «потому что мы так сказали». Я ищу "почему стандарт указывает на такое поведение". Мои предположения ниже.

Мой вопрос прост: почему указанная строка компилируется, а эквивалентная следующая строка не компилируется (и я не хочу, потому что «стандарт так говорит», я уже знаю - я не приму ответы, которые дают это как объяснение; я хотел бы получить объяснение относительно , почему стандарт так говорит).

Примечания: хотя я использую boost, boost не имеет отношения к этому вопросу, и ошибка в различных форматах была воспроизведена с использованием g ++ 4.1. * И 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;
}

Указанная строка завершается с этой ошибкой: main.C: В функции-члене 'void Derived :: test ()': main.C: 9: ошибка: 'void Base :: foo (int)' защищен main.C: 31: ошибка: в этом контексте

Как уже отмечалось, предположительно эквивалентный оператор, приведенный выше, компилируется корректно (и, если оператор-нарушитель закомментирован, выполняется с ожидаемым результатом вывода «5», «6», «7» в отдельных строках).

При поиске объяснения я наткнулся на 11.5.1 в стандарте (в частности, я смотрю на проект 2006-11-06):

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

Прочитав это, стало очевидно, почему второе утверждение провалилось, а первое - успешно, но затем возник вопрос: в чем причина этого?

Первоначально я думал, что компилятор расширяет шаблоны boost :: bind, обнаруживает, что Base :: foo защищен, и выгоняет его, потому что boost :: bind <…> не был другом. Но чем больше я думал об этом объяснении, тем меньше оно имело смысл, потому что, если я правильно помню, как только вы берете указатель на участника (предполагая, что вы изначально находитесь в управлении доступом к члену), вся информация управления доступом потерянный (т. е. я мог бы определить функцию, которая возвращает произвольный указатель на член, который поочередно возвращает открытый, защищенный или закрытый член в зависимости от некоторого ввода, и возвращающий не был бы мудрее).

Больше я думал об этом, и единственное правдоподобное объяснение, которое я мог придумать, почему это должно иметь значение, было в случае множественного наследования. В частности, в зависимости от макета класса указатель члена при вычислении из Base будет отличаться от указателя, вычисленного по Derived.

Ответы [ 3 ]

7 голосов
/ 17 июня 2010

Это все о "контексте". В первом вызове контекст вызова - Derived, который имеет доступ к защищенным членам Base и, следовательно, может принимать их адреса. Во втором контексте контекст «вне» Derived и, следовательно, вне Base, поэтому доступ защищенного члена не разрешен.

1 голос
/ 24 сентября 2014

На самом деле, это кажется логичным.Наследование дает вам доступ к Derived :: foo, а не к Base :: foo.Позвольте мне проиллюстрировать пример кода:

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);
    }
};
0 голосов
/ 29 декабря 2017

Причиной такого ограничения является обеспечение контроля доступа для разных классов, которые имеют общую базу.

Это подкреплено примечаниями в Базовый язык дефектов Отчет о дефекте # 385 , соответствующая часть скопирована здесь для справки:

[...] причина, по которой у нас есть это правило, заключается в том, что использование C унаследованных защищенных членов может отличаться от их использования в классе-брате, скажем D. Таким образом, члены и друзья C могут использовать B::p только в соответствии с использованием C, то есть в 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 - это то, что мы хотим, чтобы компилятор применял, но если скомпилировано выше, это не так.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...