Итак, давайте начнем разбираться, а какова же польза от использования константности в C++. И первое, с чего мы начнем — это то, что мы поговорим и покажем, как константность защищает нас от случайного изменения объекта в программе. Это на самом деле должно быть довольно знакомо вам, потому что мы еще в первом курсе, в белом поясе по C++ говорили о том, что константность защищает нас от случайных изменений. И давайте рассмотрим вот такой пример. Допустим, нам надо реализовать шаблон CopyIfNotEqual, который берет вектор src — входной вектор — и копирует из него все элементы в вектор dst, которые не равны параметру value. Кроме того, у нас даже есть юнит-тест один, который проверяет работоспособность нашей реализации. И вот он вызывается в функции main. Как можно эту функцию, CopyIfNotEqual, как ее можно реализовать? На самом деле для этого есть стандартный алгоритм, который реализует именно эту функциональность. Давайте мы им и воспользуемся. Этот алгоритм называется remove_copy, и воспользуемся мы им вот так: мы передадим входной диапазон src и скажем, что мы будем вставлять в конец нашего вектора dst, и значит, нас интересует вот это значение. То есть нас интересуют все элементы, которые не равны value. Написали, наш код компилируется, и давайте запустим юнит-тест, убедимся, что наша реализация правильная. Запустили юнит-тест и видим, что что-то у нас не так. Что-то у нас не так. Сработал assert, и мы вернули пустой вектор, хотя должны были вернуть вот такой вот набор элементов. Смотрите, что произошло. Только запустив программу, мы поняли, что в ней есть ошибка. И в нашем примере все было достаточно просто. Мы запустили программу, сразу увидели, что юнит-тест не сработал. На практике же эта программа может раскатиться на большое количество серверов, работать там несколько часов, дней, а может быть, и недель, и только после такого вот долгого процесса работы она вдруг может начать работать неправильно. Это следствие того, что в параметрах шаблона CopyIfNotEqual мы не используем константность. Потому что здесь я намеренно допустил опечатку. Я в back_inserter передал не dst, не выходной вектор, а входной — source, src. Если же мы входной вектор объявим, как константную ссылку, потому что это входной вектор, из которого мы только читаем и копируем элементы, то есть мы не собираемся его изменять. Если мы объявляем его константным, запускаем компиляцию, наша программа не компилируется. Таким образом компилятор нас защищает от случайного изменения объекта, который по своему смыслу не должен изменяться. Итак, мы видим, что у нас не компилируется вот здесь вот, мы замечаем, что мы опечатались и вместо dst написали src. Меняем src на dst, компилируем, компилируется, и юнит-тест проходит. Это было наглядной демонстрацией того, как константность защищает нас от случайного изменения тех объектов, которые по своему смыслу изменены быть не должны. Давайте рассмотрим другой, менее очевидный пример. Пойдем в функцию main, уберем отсюда вызов юнит-теста, он нам больше не нужен. И сделаем вот что: мы объявим переменную value. Допустим, она равна 4. И создадим лямбда-функцию, назовем ее increase, которая будет захватывать value по значению, принимать целочисленный аргумент и возвращать value + x. Что мы можем ожидать от функции increase? То, что если мы дважды вызовем ее с одним и тем же аргументом, то она нам вернет одно и то же значение. Ну, потому что это функция, и мы ожидаем, что если ей даешь одни и те же значения на вход, она будет возвращать одни и те же значения. Ну, сейчас это так и происходит, мы два раза вызвали ее от 5, и она оба раза вернула 9. Но допустим, мы с вами при реализации тела этой функции допустили опечатку и написали не value + x, а value += x. Соответственно, в таком случае последовательные вызовы функции, нашей функции increase, должны приводить к изменению переменной value, и тогда она от одних и тех же аргументов будет возвращать разные результаты. Запустим компиляцию, и видим, что наша программа не компилируется. При этом компилятор пишет нам: assignment of read-only variable 'value', то есть он говорит, что переменная value вот здесь вот, внутри тела нашей лямбды, она доступна только для чтения, ее нельзя изменять. Как это происходит? Дело в том, что лямбда-функция, когда компилятор встречает лямбда-функцию, то в процессе компилирования он создает класс, у которого имя не определено, однако у него есть оператор вызова, у него есть публичный оператор вызова. Когда мы захватываем какие-то переменные по значению в лямбда-функциях, то этим переменным соответствуют поля этого класса. И что очень важно, что оператор вызова для этого класса константный. То есть семантика лямбда-функции такова, что когда мы захватываем переменные по значению, они превращаются в поля этого класса, а так как оператор вызова у него константный, эти поля менять нельзя. И именно благодаря этой самой константности в операторе вызова, мы гарантируем, что вызов лямбда-функции с одними и теми же аргументами всегда возвращает одно и то же значение. И это обеспечивается неявной константностью оператора вызова лямбда-функции. Таким образом давайте подведем итоги этого видео. Мы с вами еще раз поговорили о том, что первое преимущество константности в C++ заключается в том, что она защищает нас от случайного изменения объектов, которые по своему смыслу не должны меняться.