65. Выполняйте настройку явно и преднамеренно
При разработке шаблона точки настройки должны быть написаны корректно, с особой тщательностью, а также ясно прокомментированы. При использовании шаблона необходимо четко знать, как именно следует настроить шаблон для работы с вашим типом, и выполнить соответствующие действия.
Распространенная ловушка при написании библиотек шаблонов заключается в наличии непреднамеренных точек настройки, т.е. точек в вашем шаблоне, где может выполняться поиск пользовательского кода и его использование, но при написании такие действия вами не подразумевались. Попасть в такую ловушку очень легко — достаточно просто вызвать другую функцию или оператор обычным путем (без полной его квалификации), и если окажется, что один из его аргументов имеет тип параметра шаблона (или связанный с ним), то будет начат поиск такого кода, зависящий от аргумента. Примеров тому множество; в частности, см. рекомендацию 58.
Поэтому лучше использовать такие точки преднамеренно. Следует знать три основных пути обеспечения точек настройки в шаблоне, решить, какой именно способ вы хотите использовать в данном месте шаблона, и корректно его закодировать. Затем проверьте, не осталось ли в вашем коде случайных точек настройки там, где вы не предполагали их наличие.
Первый способ создания точки настройки — обычный "неявный интерфейс" (см. рекомендацию 64), когда ваш шаблон просто рассчитывает на то, что тип имеет соответствующий член с данным именем:
// Вариант 1. Создание точки настройки путем требования от
// типа T "foo-совместимости", т.е. наличия функции-члена с
// данным именем, сигнатурой и семантикой
template
void Sample1(T t) {
t.foo(); // foo - точка настройки
typename T::value_type x; // Еще один пример: создание
} // точки настройки для поиска
// типа (обычно создается посредством typedef)
Для реализации первого варианта автор Sample1
должен выполнить следующие действия.
•
•
Второй вариант представляет собой использование метода "неявного интерфейса", но с функциями, не являющимися членами, поиск которых выполняется с использованием ADL[3](т.е. ожидается, что данная функция находится в пространстве имен типа, для которого выполняется инстанцирование шаблона). Именно эта ситуация и явилась основной побудительной причиной для введения ADL (см. рекомендацию 57). Ваш шаблон рассчитывает на то, что для используемого типа имеется подходящая функция с заданным именем:
// Вариант 2: Создание точки настройки путем требования от
// типа T "fоо-совместимости", т.е. наличия функции, не
// являющейся членом с данным именем, сигнатурой и
// семантикой, поиск которой выполняется посредством ADL.
// (Это единственный вариант, при котором не требуется поиск
// самого типа T.)
template
void Samplе2(T t) {
foo(t); // foo - точка настройки
cout << t; // Еще один пример - operator<< с записью в
} // виде оператора представляет собой такую же
// точку настройки
Для реализации варианта 2 автор Samplе2
должен выполнить следующие действия.