Однако у функции shorterString()
есть один потенциальный недостаток: ее вызов происходит медленнее, чем вычисление эквивалентного выражения. На большинстве машин при вызове функции осуществляется довольно много действий: перед обращением сохраняются регистры, которые необходимо будет восстановить после выхода; происходит копирование значений аргументов; управление программой переходит к новому участку кода.
Содержимое функции, объявленной shorterString()
объявлена встраиваемой, а ее вызов имеет такой вид:
cout << shorterString(s1, s2) << endl;
При компиляции тело функции окажется встроено по месту вызова, и в результате получится нечто вроде следующего:
cout << (s1.size() < s2.size() ? s1 : s2) << endl;
Таким образом, во время выполнения удастся избежать дополнительных затрат, связанных с вызовом функции shorterString()
.
Чтобы объявить функцию shorterString()
встраиваемой, в определении, перед типом возвращаемого значения, располагают ключевое слово inline
.
//
inline const string &
shorterString(const string &s1, const string &s2) {
return s1.size() <= s2.size() ? s1 : s2;
}
На самом деле механизм встраивания применяется в процессе оптимизации объектного кода, в ходе которого код небольших функций, вызов которых происходит достаточно часто, встраивается по месту вызова. Большинство компиляторов не будет встраивать рекурсивные функции. Функция на 75 строк также, вероятно, не будет встроена.
constexpr
constexpr
— это функция, которая может быть применена в константном выражении (см. раздел 2.4.4). Функция constexpr
определяется как любая другая функция, но должна соответствовать определенным ограничениям: возвращаемый тип и тип каждого параметра должны быть литералами (см. раздел 2.4.4), тело функции должно содержать только один оператор return
:
constexpr int new_sz() { return 42; }
constexpr int foo = new_sz(); //
Здесь функция new_sz
определена как constexpr
, она не получает никаких аргументов. Компилятор может проверить (во время компиляции), что вызов функции new_sz()
возвращает константное выражение, поэтому ее можно использовать для инициализации переменной constexpr
по имени foo
.
Если это возможно, компилятор заменит вызов функции constexpr
ее результирующим значением. Для этого функция constexpr
неявно считается встраиваемой.
Тело функции constexpr
может содержать другие операторы, если они не выполняют действий во время выполнения. Например, функция constexpr
может содержать пустые операторы, псевдонимы типа (см. раздел 2.5.1) и объявления using
.
Функции constexpr
позволено возвратить значение, которое не является константой:
//
constexpr size_t scale(size_t cnt) { return new_sz() * cnt; }
Функция scale()
возвратит константное выражение, если ее аргумент будет константным выражением, но не в противном случае:
int arr[scale(2)]; //
int i = 2; //
int a2[scale(i)]; //
Если передать константное выражение (такое как литерал 2
), возвращается тоже константное выражение. В данном случае компилятор заменит вызов функции scale()
результирующим значением.
Если происходит вызов функции scale()
с выражением, которое не является константным (например, объект i
типа int
), то возвращается неконстантное выражение. Если использовать функцию scale()
в контексте, требующем константного выражения, компилятор проверит, является ли результат константным выражением. Если это не так, то компилятор выдаст сообщение об ошибке.
Функция constexpr
не обязана возвращать константное выражение.
constexpr
в файлы заголовка