Внутри шаблона не вызывайте неквалифицированные не-члены-функции, если вы не намерены сделать их точками настройки
Причина
- Обеспечивать только запланированную гибкость.
- Избегать уязвимости к случайным изменениям среды.
Пример
Существует три основных способа разрешить вызывающему коду настраивать шаблон.
template<class T>
// Вызов функции-члена
void test1(T t)
{
t.f(); // требует, чтобы T предоставлял f()
}
template<class T>
void test2(T t)
// Вызов не-члена без квалификации
{
f(t); // требует, чтобы f(/*T*/) была доступна в области видимости вызывающего или в пространстве имён T
}
template<class T>
void test3(T t)
// Вызов «трейта»
{
test_traits<T>::f(t); // требует настройки test_traits<>
// для получения нестандартных функций/типов
}
Трейт — это, как правило, псевдоним типа для вычисления типа, функция constexpr для вычисления значения или традиционный шаблон трейтов для специализации под тип пользователя.
Примечание
Если вы намерены вызывать собственную вспомогательную функцию helper(t) со значением t, зависящим от параметра типа шаблона, поместите её в пространство имён ::detail и квалифицируйте вызов как detail::helper(t);. Неквалифицированный вызов становится точкой настройки, где может быть вызвана любая функция helper в пространстве имён типа t; это может приводить к проблемам, таким как непреднамеренный вызов неограниченных шаблонов функций.
Контроль
- В шаблоне помечать неквалифицированный вызов не-члена-функции, которому передаётся переменная зависимого типа, при наличии не-члена-функции с таким же именем в пространстве имён шаблона.
T.temp-hier: Правила шаблонов и иерархий
Шаблоны составляют основу поддержки обобщённого программирования в C++, а иерархии классов — основу поддержки объектно-ориентированного программирования. Оба языковых механизма могут эффективно использоваться в сочетании, однако необходимо избегать нескольких ловушек проектирования.