Избегайте хорошо видимых неограниченных шаблонов с распространёнными именами
Причина
Неограниченный аргумент шаблона идеально совпадает с чем угодно, поэтому такой шаблон может быть предпочтён более конкретным типам, требующим незначительных преобразований. Это особенно раздражает и опасно при использовании ADL. Распространённые имена делают эту проблему более вероятной.
Пример
namespace Bad {
struct S { int m; };
template<typename T1, typename T2>
bool operator==(T1, T2) { cout << "Bad\n"; return true; }
}
namespace T0 {
bool operator==(int, Bad::S) { cout << "T0\n"; return true; } // сравнение с int
void test()
{
Bad::S bad{ 1 };
vector<int> v(10);
bool b = 1 == bad;
bool b2 = v.size() == bad;
}
}
Это выводит T0 и Bad.
Итак, == в Bad был создан намеренно проблемным — но заметили бы вы проблему в реальном коде? Проблема в том, что v.size() возвращает беззнаковое целое, и для вызова локального == необходимо преобразование; == в Bad преобразований не требует. Реалистичные типы, такие как итераторы стандартной библиотеки, могут проявлять аналогичные антисоциальные тенденции.
Примечание
Если неограниченный шаблон определён в том же пространстве имён, что и тип, этот неограниченный шаблон может быть найден через ADL (как произошло в примере). То есть он хорошо виден.
Примечание
Это правило не должно было бы быть необходимым, но комитет не пришёл к согласию относительно исключения неограниченных шаблонов из ADL.
К сожалению, это правило будет давать много ложных срабатываний; стандартная библиотека широко нарушает его, размещая множество неограниченных шаблонов и типов в едином пространстве имён std.
Контроль
Помечать шаблоны, определённые в пространстве имён, где также определены конкретные типы (возможно, нецелесообразно до появления концептов).