Всегда ли код inline-функции подставляется в место вызова?

«Всегда ли код inline-функции подставляется в место вызова?» — вопрос из категории C++ Core, который задают на 25% собеседований C/C++ Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Нет. Ключевое слово inline в современном C++ является необязательной рекомендацией (hint) для компилятора, а не строгим указанием. Окончательное решение о подстановке (инлайнинге) принимает компилятор на основе своих оптимизационных эвристик.

Основные причины, по которым компилятор может проигнорировать inline:

  1. Сложность функции: Функция слишком большая или содержит циклы, рекурсию, switch.
  2. Виртуальные функции: Виртуальные вызовы обычно не инлайнятся из-за динамической диспетчеризации.
  3. Отключенная оптимизация: Например, при сборке в режиме отладки (-O0).
  4. Взятие адреса функции: Если адрес функции где-то используется, компилятору необходимо создать ее тело.

Пример:

// Объявление с inline — решение об инлайнинге за компилятором
inline int calculateSquare(int x) {
    return x * x;
}

// Компилятор, скорее всего, заинлайнит этот простой вызов:
// int result = 5 * 5;
int result = calculateSquare(5);

// Но может не инлайнить здесь из-за косвенного вызова через указатель
int (*funcPtr)(int) = &calculateSquare;
int result2 = funcPtr(10);

Современная практика: Ключевое слово inline чаще используется для разрешения нарушений ODR (One Definition Rule). Если функция определена (а не только объявлена) в заголовочном файле, ее следует помечать как inline, чтобы при включении заголовка в несколько единиц трансляции не возникало ошибок линковки из-за множественных определений. Фактическая подстановка кода — это задача оптимизатора.