В приведенном ниже коде есть два "эквивалентных" вызова 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.