# Immediately Invoked Lambda Expression (IILE)

## Почему `const` — это важно?

**Неизменяемость (immutability)** — залог надежного и понятного кода. Использование `const` там, где это возможно, помогает компилятору отлавливать ошибки и делает намерения программиста яснее. Когда другой человек читает ваш код и видит `const`, снижается когнитивная нагрузка на мозг — _«запоминать изменения данного значения не надо, расслабься»._ Принцип [const correctness](https://lifeexe-art.gitbook.io/lifeexe/code/c++/const-correctness) является одной из важнейших практик в C++.

## Проблематика

В простых случаях инициализация констант не вызывает проблем:

```cpp
const int c_maxPlayers = 100;
const double c_scaleFactor = getScaleFactor() * 1.5;
const bool c_enabled = checkFlag() || FORCE_ENABLE;
const int c_healthModifier = bHealing ? 20 : 0;
```

Но что делать, если для вычисления значения константы требуется несколько шагов, временные переменные, циклы или условия?

```cpp
float c_calculatedDamage = getBaseDamageValue();
if (targetAimed(calculatedDamage)) {
    for (int i = 0; i < c_effectCount; ++i) {
        calculatedDamage += getBonusDamage(i);
    }
}
```

Традиционные подходы — вынести логику в отдельную именованную функцию или отказаться от `const` — не всегда идеальны. Создание отдельной функции может быть избыточным, если логика используется только один раз. Отказ от `const` снижает безопасность и выразительность кода.

## Immediately Invoked Lambda Expression (IILE)

Здесь на помощь приходит использование немедленно вызываемого лямбда-выражения (IILE). Мы определяем лямбда-функцию, которая инкапсулирует всю сложную логику инициализации, и тут же вызываем её. Результат этого вызова и присваивается нашей константе.

**Как это выглядит:**

<pre class="language-cpp"><code class="lang-cpp"><strong>const auto myBeautifulLambda = [](){ return 13; }();
</strong></code></pre>

Скобочки форева :smile: Последние круглые скобки `()` после фигурных скобок лямбды — это и есть немедленный вызов. Они заставляют лямбду выполниться прямо в месте определения.

То есть мы можем проинициализировать сложный объект и сохранить константность:

```cpp
const auto c_calculatedDamage = [&]() {
  float tempDamage = getBaseDamageValue();
  if (targetAimed(tempDamage)) {
      for (int i = 0; i < c_effectCount; ++i) {
          tempDamage += getBonusDamage(i);
      }
  }
  return tempDamage;
}();
```

### Преимущества IILE для инициализации:

* **Инкапсуляция:** вся логика инициализации собрана в одном месте.
* **Локальность:** временные переменные, используемые для вычисления, не "загрязняют" внешнюю область видимости.
* **`const` сorrectness:** позволяет объявить переменную как `const` (или даже `constexpr`, если лямбда соответствует требованиям), даже если её вычисление многоэтапное.
* **Чистота кода:** избавляет от необходимости создавать отдельные, одноразовые именованные функции.

### Альтернативный синтаксис (C++17):

В **C++17** можно использовать `std::invoke`, хотя для **IILE** прямой вызов `()` обычно предпочтительнее и понятнее:

```cpp
#include <functional> 
// ...
const auto c_anotherConstant = std::invoke([] {
    // ... 
    return 13;
});
```

## Ссылки

* [**ES.28: Use lambdas for complex initialization, especially of `const` variables**](https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#es28-use-lambdas-for-complex-initialization-especially-of-const-variables)
