Действительно, они просто работают.Они используют довольно простые свойства шаблонов, которые иногда называют статическим полиморфизмом.Если вы знакомы с этим термином, это, по сути, форма утки.(Если оно похоже на утку и крякает как утка, значит, это утка)
Трюк простой.Вот очень простой пример:
template <typename T>
void say_hello(const T& t) {
t.hello();
}
Функция say_hello
не заботится о типе аргумента.Это не должно происходить из интерфейса или давать какие-либо другие "обещания" о том, что это такое.Все, что имеет значение, это то, что тип работает в этом контексте.Все, что мы делаем с типом, это вызываем его hello
функцию.Это означает, что этот код будет компилироваться для любого типа, который имеет функцию-член hello
.
Алгоритмы STL работают аналогично.Вот простая реализация std::for_each
:
template <typename iter_type, typename func_type>
void for_each(iter_type first, iter_type last, func_type f){
for (iter_type cur = first; cur != last; ++cur) {
f(*cur);
}
}
Этот код будет компилироваться всякий раз, когда типы шаблонов соответствуют предъявляемым к ним требованиям;iter_type
должен иметь преинкрементный оператор ++ -.Он должен иметь конструктор копирования и оператор! =, А также оператор * -dereference.
func_type
должен реализовывать оператор вызова функции, принимая тот же аргументвведите, как вы получите, разыменовывая объект типа iter_type
.Если я вызову for_each
с типами, которые удовлетворяют этим требованиям, код скомпилируется.iter_type
может быть любого типа, который удовлетворяет этим требованиям.В коде нет ничего, что говорило бы: «это должно работать с векторными итераторами, итераторами списков и итераторами карт».Но пока итераторы vector, list или map реализуют используемые нами операторы, это будет работать.