Давайте ещё раз поговорим о том, как связаны между собой шаблон класса и класс. Итак, в нашем примере из предыдущего видео с функцией Head мы с вами написали шаблон класса IteratorRange, и это именно шаблон класса, это не сам класс, это не самостоятельный тип, а просто шаблон, в который надо сначала подставить какой-то конкретный тип, чтобы из него создать класс. IteratorRange у нас проитеризован типом итератора, поэтому, чтобы создать из него какой-то конкретный класс, в него надо подставить какой-то итератор, например итератор вектора целых чисел. И вот тогда вот эта вот вещь целиком — IteratorRange от vector int integrator — тогда это целиком получится самостоятельный отдельный класс. Мне кажется, что этот синтаксис, когда мы пишем какое-то слово, потом в угловых скобках указываем какой-то тип и получаем какой-то класс, какой-то самостоятельный тип, вот этот синтаксис вам может быть очень, вернее, даже должен быть очень вам знаком. Собственно, давайте посмотрим. Вот у нас есть наши стандартные vector, map и set. И мы берём и в угловых скобках добавляем к ним какие-то типы. У нас получается vector int, map из строки vector int и, например, множество строк. И, собственно, этим примером я хочу просто сказать, что все стандартные контейнеры, которыми мы с вами пользовались — vector, map, set, para, deck — все эти контейнеры являются шаблонами классов. То есть в библиотеке C++ это шаблоны классов, которые, собственно, при подстановке туда типа, генерируют вам тип контейнера для конкретного типа элементов. Так что теперь вы знаете эту вещь, что стандартные контейнеры являются шаблонами классов. И это позволит вам проще читать документацию. Вот у меня открыта документация на map. И вы теперь можете понимать, что, собственно, здесь написано. Вы видите, что у нас есть класс map, но это не просто класс, а это шаблон класса, потому что он начинается с ключевого слова template и дальше перечислены его параметры. class Key — это ключ, class T — это значение, и также у него есть ещё два шаблонных параметра, это компоратор и аллокатор, мы пока их не рассматривали и, тем более, у них есть значения по умолчанию. Поэтому если вы их не указываете, то у вас всё равно всё работает. Кроме того, на что ещё нужно обратить внимание? Что когда у нас есть какой-то шаблон класса, в данном случае — vector, и мы к этом шаблону подставляем разные конкретные типы, то есть мы этот шаблон инстанцируем с разными типами. Вот как в данном случае мы инстанцировали шаблон vector c параметрами c типом string, int и double. В этом случае у нас получаются разные типы, то есть vector int и вектор string — это совершенно разные с точки зрения компилятора C++ типы. Что я имею ввиду? То есть как это проявляется на практике? Но вот смотрите. У нас есть, например, vector int V. И у нас есть, например, vector double D. Мы не можем написать V равно D, потому что это два разных типа, вектор целых числе и вектор дробных чисел. Вот, собственно, нам об этом и компилятор говорит, что нет оператора равно для вектора int и для вектора double. Так что просто вы должны вот эту концептуальную вещь усвоить, что когда мы из шаблона класса подставляем туда разные типы, когда мы инстанцируем с разными типами, получаются разные классы. И ещё одна важная вещь. Вот у нас есть IteratorRange, диапазон двух итераторов. Мы можем захотеть написать функцию, которая считает размер этого диапазона, сколько в этом диапазоне элементов. Мы не можем написать её вот так. Значит, например, size_t RangeSize IteratorRange r return r.end минус r.begin. И давайте ею воспользуемся, выведем RangeSize от Head v 3. В данном случае мы ожидаем увидить 3, но наша программа просто не компилируется. И не компилируется она с какими-то непонятными сообщениями об ошибке. В данном случае из этих сообщений трудно что-то понять, но смотрите: мы объявили функцию и у неё есть параметр r. У этого параметра должен быть какой-то тип. Мы указали IteratorRange. IteratorRange — это не тип, это шаблон типа. Чтобы из этого шаблона создать конкретный тип, его надо истанцировать. Поэтому чтобы у нас была функция RangeSize, мы что должны сделать? Мы должны сделать её шаблонной и проинстанцировать вот этот шаблон IteratorRange с помощью типа T. Вот эта запись — IteratorRange от T — это уже конкретный тип. И поэтому в таком виде наша программа компилируется, работает и выводит 3. То есть вот эта важная вещь, которую вы тоже должны усвоить, что шаблон класса — это шаблон, а класс — это та вещь, которая из шаблона получается. Нельзя использовать просто шаблон без инстанцирования в том месте, где язык ожидает увидить тип.