Когда я должен написать ключевое слово inline
для функции / метода в C ++?
Увидев некоторые ответы, некоторые связанные вопросы:
Когда я должен не написать ключевое слово «встроенный» для функции / методы в C ++?
Когда компилятор не будет знать, когда сделать функцию / метод «встроенным»?
Имеет ли значение, если приложение является многопоточным, когда кто-то пишет «inline» для функции / метода?
c++
inline
one-definition-rule
частичный
источник
источник
inline
(9.3 / 2).Ответы:
О, чувак, один из моих любимых мозолей.
inline
больше похожstatic
илиextern
чем директивы , говорящих компилятор встраивать свои функции.extern
,static
,inline
Являются директивы сцепления, используется почти исключительно линкер, а не компилятор.Сказано, что
inline
подсказывает компилятору, что вы думаете, что функция должна быть встроенной. Возможно, это было правдой в 1998 году, но спустя десятилетие компилятор не нуждается в таких подсказках. Не говоря уже о том, что люди обычно ошибаются, когда речь заходит об оптимизации кода, поэтому большинство компиляторов игнорируют «подсказку».static
- имя переменной / функции нельзя использовать в других единицах перевода. Линкер должен убедиться, что он случайно не использует статически определенную переменную / функцию из другого модуля перевода.extern
- используйте это имя переменной / функции в этом модуле перевода, но не жалуйтесь, если оно не определено. Компоновщик разберется и убедится, что весь код, который пытался использовать какой-либо символ extern, имеет свой адрес.inline
- эта функция будет определена в нескольких единицах перевода, не беспокойтесь об этом. Компоновщик должен убедиться, что все единицы перевода используют один экземпляр переменной / функции.Примечание. Как правило, декларировать шаблоны
inline
бессмысленно, поскольку они уже имеют семантику связыванияinline
. Однако явная специализация и конкретизация шаблонов требуютinline
, чтобы использовать.Конкретные ответы на ваши вопросы:
Только когда вы хотите, чтобы функция была определена в заголовке. Точнее, только когда определение функции может отображаться в нескольких единицах перевода. Хорошей идеей будет определить небольшие (как в одном вкладыше) функции в заголовочном файле, поскольку это дает компилятору больше информации для работы при оптимизации кода. Это также увеличивает время компиляции.
Не добавляйте inline только потому, что вы думаете, что ваш код будет работать быстрее, если его встроит компилятор.
Как правило, компилятор сможет сделать это лучше, чем вы. Однако у компилятора нет возможности встроить код, если у него нет определения функции. В максимально оптимизированном коде обычно все
private
методы встроены независимо от того, просите вы об этом или нет.В качестве отступления для предотвращения встраивания в GCC, используйте
__attribute__(( noinline ))
и в Visual Studio, используйте__declspec(noinline)
.Многопоточность никак не влияет на вставку.
источник
inline
ключевое слово не связаны. У вас есть правильная идея, хотя. Как правило, угадывание того, что было бы улучшено путем встраивания, очень подвержено ошибкам. Исключением из этого правила является один вкладыш.Я хотел бы внести свой вклад во все замечательные ответы в этой теме убедительным примером, чтобы рассеять все оставшиеся недоразумения.
Даны два исходных файла, такие как:
inline111.cpp:
inline222.cpp:
Дело А:
Компилировать :
Выход :
Обсуждение :
Даже если у вас должны быть идентичные определения ваших встроенных функций, компилятор C ++ не помечает его, если это не так (на самом деле, из-за отдельной компиляции у него нет способов проверить это). Это ваша собственная обязанность обеспечить это!
Линкер не жалуется на Одно Правило Определения , как
fun()
объявлено какinline
. Однако, поскольку inline111.cpp является первым модулем трансляции (который фактически вызываетfun()
), обработанным компилятором, экземпляр компилятора создаетсяfun()
при первом обращении к вызову в inline111.cpp . Если компилятор решает не расширятьfun()
свой вызов из любого места в вашей программе ( например, из inline222.cpp ), вызовfun()
всегда будет связан с его экземпляром, созданным из inline111.cpp (вызовfun()
внутри inline222.cpp).может также создать экземпляр в этом модуле перевода, но он останется несвязанным). Действительно, это видно из одинаковых&fun = 0x4029a0
распечаток.Наконец, несмотря на
inline
предложение компилятору фактически расширить однострочникfun()
, он полностью игнорирует ваше предложение, что ясно изfun() = 111
обеих строк.Дело Б:
Компилировать (обратите внимание в обратном порядке) :
Выход :
Обсуждение :
Этот случай утверждает , что уже обсуждалось в случае А .
Обратите внимание на важный момент: если вы закомментируете фактический вызов
fun()
в inline222.cpp ( например, полностью закомментируйтеcout
-statement в inline222.cpp ), то, несмотря на порядок компиляции ваших единиц перевода,fun()
он будет создан при первом обращении к нему в inline111.cpp , что приводит к распечатке для дела B asinline111: fun() = 111, &fun = 0x402980
.Дело С:
Компилировать (уведомление -O2) :
или
Выход :
Обсуждение :
-O2
оптимизация побуждает компилятор фактически расширять функции, которые могут быть встроены (обратите внимание также, что-fno-inline
это по умолчанию без параметров оптимизации). Как видно из приведенного здесь фрагмента,fun()
фактически он был расширен (в соответствии с его определением в этой конкретной единице перевода), что привело к двум различнымfun()
распечаткам. Несмотря на это, все еще существует только один глобально связанный экземплярfun()
(как того требует стандарт), как видно из идентичной&fun
распечатки.источник
inline
функции неопределенным поведением..cpp
них является отдельной единицей перевода. Желательно добавлять случаи для-flto
включения / выключения.Вам все еще нужно явно встроить свою функцию при выполнении специализации шаблона (если специализация находится в файле .h)
источник
1) В настоящее время почти никогда. Если это хорошая идея встроить функцию, компилятор сделает это без вашей помощи.
2) Всегда. Смотрите № 1.
(Отредактировано, чтобы отразить, что вы разбили свой вопрос на два вопроса ...)
источник
inline
все еще необходимо, например, чтобы определить функцию в заголовочном файле (и это требуется для встраивания такой функции в несколько модулей компиляции).inline
спецификатор, ее экземпляры автоматически объединяются компоновщиком в один, а ODR не используется.Если функция объявлена в заголовке и определена в
.cpp
файле, вам не следует писать ключевое слово.Там нет такой ситуации. Компилятор не может сделать функцию встроенной. Все, что он может сделать, это встроить некоторые или все вызовы функции. Это не может быть сделано, если у него нет кода функции (в этом случае компоновщик должен сделать это, если он может это сделать).
Нет, это не имеет значения.
источник
Это зависит от используемого компилятора. Не стоит слепо полагать, что в настоящее время компиляторы лучше, чем люди, знают, как встроить код, и вам никогда не следует использовать его по соображениям производительности, потому что это директива о связях, а не подсказка по оптимизации. Хотя я согласен с тем, что идеологически правильны эти аргументы, столкновение с реальностью может быть другим.
После прочтения нескольких потоков я из любопытства попробовал влияние встроенного кода на код, над которым я только что работал, и в результате я получил измеримое ускорение для GCC и отсутствие ускорения для компилятора Intel.
(Более подробно: математическое моделирование с несколькими критическими функциями, определенными вне класса, GCC 4.6.3 (g ++ -O3), ICC 13.1.0 (icpc -O3); добавление встроенных в критические точки вызвало + 6% ускорение с помощью кода GCC).
Таким образом, если вы квалифицируете GCC 4.6 как современный компилятор, то результат в том, что встроенная директива все еще имеет значение, если вы пишете задачи с интенсивным использованием процессора и знаете, где именно находится узкое место.
источник
На самом деле, почти никогда. Все, что вы делаете, это предлагаете компилятору сделать данную функцию встроенной (например, замените все вызовы этой функции / ее тело). Конечно, нет никаких гарантий: компилятор может игнорировать директиву.
Компилятор, как правило, хорошо справляется с обнаружением и оптимизацией подобных вещей.
источник
inline
есть семантическое различие (например, в способе обработки нескольких определений), что важно в некоторых случаях (например, шаблоны).Я проверил это для Visual Studio 9 (15.00.30729.01), скомпилировав с / FAcs и посмотрев код сборки: компилятор вызывал функции-члены без оптимизации, включенной в режиме отладки . Даже если функция помечена __forceinline , встроенный код времени выполнения не создается.
источник
Вы хотите поместить это в самом начале, перед возвращаемым типом. Но большинство компиляторов игнорируют это. Если он определен и имеет меньший блок кода, большинство компиляторов все равно считают его встроенным.
источник
Если вы не пишете библиотеку или у вас нет особых причин, вы можете забыть об этом
inline
и использовать вместо этого оптимизацию времени соединения . Это устраняет требование о том, что определение функции должно быть в заголовке, чтобы его можно было рассмотреть для встраивания между модулями компиляции, и это именно то, чтоinline
позволяет.(Но посмотрите, есть ли причина, почему бы не использовать оптимизацию по времени ссылки? )
источник
В линиюКлючевое слово запрашивает компилятор заменить вызов функции на тело функции, сначала он вычисляет выражение, а затем передает его. Он уменьшает накладные расходы на вызов функции, так как нет необходимости сохранять адрес возврата и стек памяти не требуется для функции аргументы.
Когда использовать:
источник
inline
или нет в C и C ++. C Инлайн: stackoverflow.com/a/62287072/7194773 C ++ рядный: stackoverflow.com/a/62230963/7194773C ++ inline полностью отличается от C inline .
inline
Сам по себе влияет на компилятор, ассемблер и компоновщик. Это директива для компилятора, в которой говорится, что для этой функции / данных следует использовать только символ, если он используется в модуле перевода, и если он есть, то, как и методы класса, указать ассемблеру хранить их в разделе.section .text.c::function(),"axG",@progbits,c::function(),comdat
или.section .bss.i,"awG",@nobits,i,comdat
для данных. Шаблонные экземпляры также идут в своих собственных группах comdat.Это следует
.section name, "flags"MG, @type, entsize, GroupName[, linkage]
. Например, имя раздела.text.c::function()
.axG
означает, что раздел является размещаемым, исполняемым и в группе, т. е. будет указано имя группы (и флаг М отсутствует, поэтому entsize не будет указан);@progbits
означает, что раздел содержит данные и не является пустым;c::function()
это имя группы, и группа имеетcomdat
связь означает, что во всех объектных файлах все разделы, встречающиеся с этим именем группы, помеченные с помощью comdat, будут удалены из конечного исполняемого файла, кроме 1, т.е. компилятор удостоверяется, что в модуле перевода есть только одно определение, а затем говорит ассемблеру поместить он находится в своей собственной группе в объектном файле (1 раздел в 1 группе), а затем компоновщик убедится, что если в каких-либо объектных файлах есть группа с таким же именем, то в окончательный .exe-файл включается только один. Разница междуinline
и неиспользованиемinline
теперь видна ассемблеру и, как следствие, компоновщику, потому что он не сохраняется в обычном.data
или.text
т. Д. Ассемблером из-за их директив.static inline
в классе это означает определение типа, а не объявление (позволяет определить статический член в классе) и сделать его встроенным; теперь он ведет себя как выше.static inline
На область видимости файла влияет только компилятор. Для компилятора это означает: испускать символ для этой функции / данных, только если он используется в модуле перевода, и делать это как обычный статический символ (хранить в .text /.data без директивы .globl). На ассемблере нет теперь никакой разницы междуstatic
иstatic inline
extern inline
является объявлением, которое означает, что вы должны определить этот символ в модуле перевода или выдать ошибку компилятора; если он определен, то рассматривайте его как обычный,inline
и для ассемблера и компоновщика не будет никакой разницы междуextern inline
иinline
, так что это только защита компилятора.Все вышеперечисленное без строки ошибки сворачивается в
inline int i[5]
. Очевидно, что если вы сделалиextern inline int i[] = {5};
тогдаextern
будут игнорироваться из-за явного определения через присваивание.inline
в пространстве имен, увидеть это и этоисточник
При разработке и отладке кода
inline
не используйте. Это усложняет отладку.Основная причина их добавления - помочь оптимизировать сгенерированный код. Обычно это заменяет увеличенное пространство кода на скорость, но иногда
inline
экономит пространство кода и время выполнения.Подобная мысль об оптимизации производительности до завершения алгоритма является преждевременной оптимизацией .
источник
inline
функции обычно не являются встроенными, если они не компилируются с оптимизацией, поэтому они никак не влияют на отладку. Помните, что это подсказка, а не требование.inline
функции были встроены. Невозможно было установить в них значимые контрольные точки.inline
ничего не сделает для улучшения кода на современном компиляторе, который может выяснить, встроен он или нет сам по себе.Когда следует включить:
1. Когда нужно избежать издержек, возникающих при вызове функции, такой как передача параметров, передача управления, возврат управления и т. Д.
2.Функция должна быть небольшой, часто вызываться, и создание inline действительно выгодно, так как в соответствии с правилом 80-20, постарайтесь сделать функцию inline встроенной, что существенно повлияет на производительность программы.
Как мы знаем, inline - это просто запрос к компилятору, похожий на регистр, и он будет стоить вам при размере кода объекта.
источник
inline
утратил свой статус подсказки по оптимизации, и большинство компиляторов используют его только для учета нескольких определений, как это должно быть в IMO. Более того, начиная с C ++ 11,register
он полностью устарел за его прежнее значение «я знаю лучше, чем компилятор, как оптимизировать»: теперь это просто зарезервированное слово без текущего значения.inline
в некоторой степени.Встроенная функция C ++ - это мощная концепция, которая обычно используется с классами. Если функция встроенная, компилятор помещает копию кода этой функции в каждую точку, где функция вызывается во время компиляции.
Любое изменение встроенной функции может потребовать перекомпиляции всех клиентов функции, поскольку компилятору потребуется заменить весь код еще раз, иначе он продолжит работу со старой функциональностью.
Чтобы встроить функцию, поместите ключевое слово inline перед именем функции и определите функцию перед выполнением каких-либо вызовов этой функции. Компилятор может игнорировать встроенный спецификатор, если определенная функция больше строки.
Определение функции в определении класса является встроенным определением функции, даже без использования встроенного спецификатора.
Ниже приведен пример, который использует встроенную функцию для возврата максимум двух чисел
Для получения дополнительной информации см. здесь .
источник