В вашем примере тип параметра S
в foo2
равен C1
.Отношения между друзьями существуют между T2<S>
и T1<S>
.Хотя C1
происходит от T1<C1>
, оно не становится другом всех друзей T1<C1>
.
В выражении pT1->bar
, bar
находится в C1
и, какДружба не передается производному классу, она является закрытой.
Ваш пример использует виртуальные функции, так что это можно решить, явно указав bar
, что является другомнаш класс:
void foo2(S *pT1) { pT1->template T1<S>::bar(); }
Проверка доступа теперь прошла успешно.
Ссылка для этого в стандарте '03 C ++ приведена в 11.4 / 10, где просто говорится:
Дружба не является ни наследственной, ни переходной.
Спасибо Potatoswatter за его комментарий.Используя qualified-id
для id-expession
, мы отключаем виртуальную диспетчеризацию (5.2.2 / 1):
Если выбранная функция не виртуальная или если id-выражение вВыражение доступа к члену класса является квалифицированным идентификатором, эта функция вызывается.
Мы можем добавить не виртуальный диспетчер, или мы можем сначала преобразовать параметр в базовый тип, а затем сделать вызов:
void foo2(S *pT1) { static_cast< T1<S>* > (pT1)->bar(); }
Виртуальная отправка теперь происходит по мере необходимости.