Когда следует писать ключевое слово inline
для функции/метода в С++?
После просмотра некоторых ответов некоторые связанные вопросы:
Когда я должен не писать ключевое слово 'inline' для функции/метода в С++?
Когда компилятор не знает, когда нужно сделать функцию/метод 'inline'?
Имеет ли значение, если приложение многопоточно, когда вы пишете 'inline' для функции/метода?
О человек, один из моих питомцев.
inline
больше похожа на static
или extern
чем на директиву, сообщающую компилятору встроить ваши функции. extern
, static
, inline
- это директивы привязки, используемые почти исключительно компоновщиком, а не компилятором.
Говорят, что inline
подсказки компилятору, что вы считаете, что функция должна быть встроена. Это могло быть правдой в 1998 году, но десятилетие спустя компилятор не нуждается в таких подсказках. Не говоря уже о том, что люди обычно ошибаются, когда дело доходит до оптимизации кода, поэтому большинство компиляторов полностью игнорируют "подсказку".
static
- имя переменной/функции не может использоваться в других единицах перевода. Линкером необходимо убедиться, что он случайно не использует статически определенную переменную/функцию из другой единицы перевода.
extern
- используйте это имя переменной/функции в этой единицы перевода, но не жалуйтесь, если оно не определено. Компонент будет сортировать его и убедиться, что весь код, который пытался использовать какой-либо символ extern, имеет свой адрес.
inline
- эта функция будет определена в нескольких единицах перевода, не беспокойтесь об этом. Компилятор должен убедиться, что все единицы перевода используют один экземпляр переменной/функции.
Примечание. Как правило, объявление шаблонов inline
бессмысленно, поскольку они уже имеют семантику привязки inline
. Однако для explicit
специализации и создания экземпляров шаблонов требуется inline
.
Конкретные ответы на ваши вопросы:
Когда следует писать ключевое слово 'inline' для функции/метода в C++?
Только когда вы хотите, чтобы функция была определена в заголовке. Точнее, только когда определение функции может отображаться в нескольких единицах перевода. Это хорошая идея, чтобы определить малые (как в одном лайнере) функции в файле заголовка, поскольку он дает компилятору больше информации для работы с оптимизацией кода. Это также увеличивает время компиляции.
Когда я не должен писать ключевое слово 'inline' для функции/метода в C++?
Не добавляйте встроенную строку только потому, что считаете, что ваш код будет работать быстрее, если компилятор включит его.
Когда компилятор не знает, когда нужно сделать функцию/метод "inline"?
Как правило, компилятор сможет сделать это лучше вас. Однако компилятор не имеет возможности встроить код, если он не имеет определения функции. В максимально оптимизированном коде обычно используются все private
методы, независимо от того, запрашиваете вы это или нет.
Для предотвращения встраивания в GCC используйте __attribute__(( noinline ))
, а в Visual Studio используйте __declspec(noinline)
.
Имеет ли значение, если приложение многопоточно, когда вы пишете 'inline' для функции/метода?
Многопоточность никоим образом не влияет на inlining.
Вам все равно нужно явно встраивать свою функцию при выполнении специализированной специализации (если специализация находится в файле .h)
Я хотел бы внести свой вклад во все замечательные ответы в этом потоке с убедительным примером, чтобы разогнать все оставшиеся недоразумения.
Для двух исходных файлов, таких как:
inline111.cpp:
#include <iostream>
void bar();
inline int fun() {
return 111;
}
int main() {
std::cout << "inline111: fun() = " << fun() << ", &fun = " << (void*) &fun;
bar();
}
inline222.cpp:
#include <iostream>
inline int fun() {
return 222;
}
void bar() {
std::cout << "inline222: fun() = " << fun() << ", &fun = " << (void*) &fun;
}
Случай A:
Compile:
g++ -std=c++11 inline111.cpp inline222.cpp
Вывод:
inline111: fun() = 111, &fun = 0x4029a0
inline222: fun() = 111, &fun = 0x4029a0
Обсуждение:
Даже вы должны иметь одинаковые определения своего встроенного функции, компилятор С++ не указывает его, если это не так (на самом деле, из-за отдельной компиляции он не имеет возможности проверить его). Это ваша обязанность обеспечить это!
Линкера не жалуется на правило определения, поскольку fun()
объявляется как inline
. Однако, поскольку inline111.cpp является первой единицей перевода (которая фактически вызывает fun()
), обработанной компилятором, компилятор создает экземпляр fun()
при первом вызове вызова в inline111.cpp. Если компилятор решает не расширять fun()
при его вызове из любого места в вашей программе (например, из inline222.cpp), вызов fun()
всегда будет связан к его экземпляру, созданному из inline111.cpp (вызов fun()
внутри inline222.cpp может также привести экземпляр в этой единицы перевода, но он останется несвязанным). Действительно, это видно из идентичных распечаток &fun = 0x4029a0
.
Наконец, несмотря на предложение inline
компилятору фактически развернуть однострочный fun()
, он полностью игнорирует ваше предложение, что ясно, потому что fun() = 111
в обеих строках.
Случай B:
Скомпилируйте (обратите внимание на обратный порядок):
g++ -std=c++11 inline222.cpp inline111.cpp
Вывод:
inline111: fun() = 222, &fun = 0x402980
inline222: fun() = 222, &fun = 0x402980
Обсуждение:
Этот случай утверждает, что обсуждалось в случае A.
Обратите внимание на важный момент: если вы закомментируете фактический вызов fun()
в inline222.cpp (например, комментарий out cout
-statement в inline222.cpp), то, несмотря на порядок компиляции ваших единиц перевода, fun()
будет создан при первом вызове вызова в inline111.cpp, что приведет к распечатке для Case B как inline111: fun() = 111, &fun = 0x402980
.
Случай C:
Скомпилировать (уведомление -O2):
g++ -std=c++11 -O2 inline222.cpp inline111.cpp
или
g++ -std=c++11 -O2 inline111.cpp inline222.cpp
Вывод:
inline111: fun() = 111, &fun = 0x402900
inline222: fun() = 222, &fun = 0x402900
Обсуждение:
-O2
побуждает компилятор фактически расширять функции, которые могут быть встроены (обратите внимание также, что -fno-inline
по умолчанию без параметров оптимизации). Как видно из отпечатка здесь, fun()
фактически был встроенным расширением (в соответствии с его определением в этой конкретной единицы перевода), что привело к двум различным отпечаткам fun()
. Несмотря на это, существует еще один глобально связанный экземпляр fun()
(как требуется стандартом), как видно из идентичного отпечатка &fun
.inline
функции неопределенным поведением.
1) В наше время почти никогда. Если это хорошая идея встроить функцию, компилятор сделает это без вашей помощи.
2) Всегда. См. № 1.
(Отредактировано с учетом того, что вы нарушили свой вопрос на два вопроса...)
Когда я не должен писать ключевое слово 'inline' для функции/метода в С++?
Если функция определена в файле .cpp
, вы не должны писать ключевое слово.
Когда компилятор не знает, когда нужно сделать функцию/метод 'inline'?
Такой ситуации нет. Компилятор не может сделать функцию встроенной. Все, что он может сделать, это вставить некоторые или все вызовы функции. Он не может этого сделать, если у него нет кода функции (в этом случае компоновщик должен сделать это, если он в состоянии это сделать).
Имеет ли значение, если приложение многопоточно, когда вы пишете 'inline' для функции/метода?
Нет, это вообще не имеет значения.
Это зависит от используемого компилятора. Не слепо верите, что в наши дни компиляторы лучше знают людей, как внедряться, и вы никогда не должны использовать его по соображениям производительности, потому что это директива linkage, а не подсказка оптимизации. Хотя я согласен с тем, что идеологически эти аргументы правильной встречей с реальностью могут быть иными.
После прочтения нескольких потоков вокруг я искал из любопытства эффекты встроенного кода, который я только что работал, и результаты заключались в том, что я получил измеримое ускорение для GCC и не ускоряюсь для компилятора Intel.
(Более подробно: математическое моделирование с несколькими критическими функциями, определенными вне класса, GCC 4.6.3 (g++ -O3), ICC 13.1.0 (icpc-O3), добавление строки в критические точки вызвало + 6% ускорение с кодом GCC).
Итак, если вы квалифицируете GCC 4.6 как современный компилятор, результатом является то, что директива inline все еще имеет значение, если вы пишете интенсивные задачи с ЦП и знаете, где именно находится узкое место.
В действительности, почти никогда. Все, что вы делаете, означает, что компилятор делает заданную функцию встроенной (например, заменяет все вызовы на эту функцию /w ее тело). Конечно, нет никаких гарантий: компилятор может игнорировать директиву.
Компилятор обычно хорошо справляется с обнаружением + оптимизации таких вещей.
inline
имеет семантическое различие в C ++ (например, в способе обработки нескольких определений), что важно в некоторых случаях (например, шаблоны).
gcc по умолчанию не выполняет никаких функций при компиляции без оптимизация включена. Я не знаю о visual studio - deft_code
Я проверил это для Visual Studio 9 (15.00.30729.01), скомпилировав /FAcs и посмотрев на код сборки: Компилятор создал вызовы функций-членов без оптимизации в режиме отладки. Даже если функция отмечена __ forceinline, не создается встроенный код времени выполнения.
При разработке и отладке кода оставьте inline
вне. Это усложняет отладку.
Основная причина их добавления - помочь оптимизировать сгенерированный код. Обычно эта торговля увеличивает пространство кода для скорости, но иногда inline
сохраняет как пространство, так и время выполнения.
Рассекая эту мысль об оптимизации производительности до завершения алгоритма преждевременная оптимизация.
inline
функции обычно не встроены, если не компилируются с оптимизацией, поэтому они никак не влияют на отладку. Помните, что это подсказка, а не требование.
Вы хотите поместить его в самом начале, перед типом возврата. Но большинство компиляторов игнорируют это. Если он определен и имеет меньший блок кода, большинство компиляторов все равно считают его inline.
Если вы не пишете библиотеку или не имеете особых причин, вы можете забыть о inline
и использовать оптимизацию времени соединения. Он устраняет требование о том, чтобы определение функции должно быть в заголовке, чтобы оно учитывалось для встраивания в единицы компиляции, что и позволяет inline
.
(Но см. Есть ли причина, почему бы не использовать оптимизацию времени ссылки?
Когда нужно встроить:
1.Когда кто-то хочет избежать накладных расходов на вещи, когда функция вызывается как передача параметров, передача управления, возврат управления и т.д.
2. Функция должна быть маленькой, часто вызываемой и сделать встроенную, действительно выгодной, поскольку в соответствии с правилом 80-20 попытайтесь сделать эту функцию встроенной, что существенно повлияет на производительность программы.
Как мы знаем, inline - это просто запрос к компилятору, похожий на регистр, и он будет стоить вам при размере Object-кода.
inline
утратил свой статус подсказки по оптимизации, и большинство компиляторов используют его только для учета нескольких определений, как это должно быть в IMO. Более того, начиная с C ++ 11, register
полностью устарела из-за своего прежнего значения «я знаю лучше, чем компилятор, как оптимизировать»: теперь это просто зарезервированное слово без текущего значения.
inline
в некоторой степени.