Что делает встроенное ключевое слово?
Современные компиляторы пытаются сбалансировать затраты на вставку функции с преимуществами.
Как преимущества, так и затраты довольно очевидны: когда функция встроена, нет необходимости в вызове функции (так как нет вызова функции), и компилятор может оптимизировать тело функции на основе в контексте, в котором он вызывается (поскольку, когда он встроен, он знает этот контекст).
Затраты могут включать в себя увеличение размера исполняемого файла (если это большая функция) и увеличение количества экземпляров сборки для тела функции, наложенной вокруг исполняемого файла.
Практическое правило: если функция большая или сложная, она, вероятно, не будет встроенной. Если он маленький, он, вероятно, будет встроен.
Это хорошо. Это предотвращает раздутые исполняемые файлы, но все же устраняет почти все накладные расходы, связанные с использованием функций Время выполнения большой, сложной функции часто карлики стоимость вызова функции, и ее включение будет иметь лишь минимальную выгоду.
Так что же делает inline
? 1017 * Компилятор вычисляет, насколько сложна функция, когда решает встроить ее. Затем он сравнивает этот расчет с некоторым порогом. Если функция менее сложна, чем пороговое значение, она указывает на функцию.
Ключевое слово inline
в основном поднимает порог для этой конкретной функции, но то, что она на самом деле делает под капотом, варьируется от компилятора к компилятору.
Можно ли встраивать все вызовы функций?
Если компилятор не знает, какая функция вызывается, он не может встроить ее.
Давайте рассмотрим пример:
// func_t is a pointer to a function that returns an integer
using func_t = int(*)();
int getNumber(func_t func) {
// The compiler can't inline func(), because it doesn't know
// what func *is*
return func();
}
Как это относится к виртуальным функциям?
Виртуальный вызов функции очень похож на вызов указателя функции, но с некоторыми ключевыми отличиями. Если вы вызываете его из базового класса, компилятор не знает заранее, какую функцию вызывать:
class Base {
virtual int getNum() { return 0; }
};
class Derived {
int value;
void setValue(int v) { value = v; }
int getNum() override { return value; }
};
int getNumFrom(Base& base) {
// The compiler doesn't know whether to call
// Base::getNum() or Derived::getNum(), so it can't inline it
return base.getNum();
}
Однако, если вы вызываете его из конкретного экземпляра класса (не ссылки и не указателя, компилятор точно знает, какая версия вызывается:
int getNumFromDerived() {
Derived d;
// Here, we know that we're calling Derived::getNum()
// so the compiler *can* inline it.
return d.getNum();
}
Как применять встроенное ключевое слово?
Вы можете указать его как в базовом классе, так и в производном классе. Просто знайте, что не будет гарантировать, что они встроены, именно потому, что иногда невозможно встроить вызов виртуальной функции.
Есть ли альтернативы?
Поскольку шаблоны сохраняют информацию о типе, компилятор всегда знает, какую функцию вызывать. Легко встроить вызовы шаблонных функций, и их использование не приведет к дополнительным расходам в вашей программе.
Если возможно, предпочтите шаблоны виртуальному наследованию.