}
...
};
Как следует из комментария, этот код просто не имеет смысла, если базовым классом является MsgSender
Чтобы исправить ситуацию, нужно как-то заставить C++ отказаться от догмы «не заглядывай в шаблонные базовые классы». Добиться этого можно тремя способами. Во-первых, можно предварить обращения к функциям из базового класса указателем this:
template
class LoggingMsgSender: public MsgSender
public:
...
void sendClearMsg(const MsgInfo& info)
{
;
this->sendClear(info); // порядок! Предполагается, что
// sendClear будет унаследована
;
}
...
};
Во-вторых, можно воспользоваться using-объявлением. Мы уже обсуждали эту тему в правиле 33, где было сказано, что using-объявлением делает скрытые имена из базового класса видимыми в производном классе. Поэтому мы можем переписать sendClearMsg следующим образом:
template
class LoggingMsgSender: public MsgSender
public:
using MsgSender
... // sendClear есть в базовом классе
void sendClearMsg(const MsgInfo& info)
{
...
sendClear(info); // нормально, предполагается, что
... // sendClear будет унаследована
}
...
};
Хотя using-объявление будет работать как здесь, так и в правиле 33, но используются они для решения разных задач. Здесь проблема не в том, что имена из базового класса скрыты за именами, объявленными в производном классе, а в том, что компилятор вообще не станет производить поиск в области видимости базового класса, если только вы явно не попросите его об этом.
И последний способ заставить ваш код компилироваться – явно указать, что вызываемая функция находится в базовом классе:
template
class LoggingMsgSender: public MsgSender
pubilc:
...
void sendClearMsg(const MsgInfo& info)
{
...
MsgSender
... // sendClear будет унаследована
}
...
};
Но этот способ хуже прочих, посколько если вызываемая функция виртуальна, то явная квалификация отключает динамическое связывание.
С точки зрения видимости имен, все три подхода эквивалентны: они обещают компилятору, что любая специализация шаблона базового класса будет поддерживать интерфейс, предоставленный общим шаблоном. Такое обещание – это все, что необходимо компилятору во время синтаксического анализа производного шаблонного класса, подобного LoggingMsgSender, но если данное обещание не будет выполнено, истина всплывет позже. Например, если в программе есть такой код:
LoggingMsgSender
MsgInfo msgData;
... // поместить info в msgData
zMsgSender.sendClearMsg(msgData); // ошибка! не скомпилируется
то вызов sendClearMsg не скомпилируется, потому что в этой точке компилятор знает, что базовый класс – это специализация шаблона MsgSender