Что означает встроенная функция C ++? - PullRequest
24 голосов
/ 01 октября 2008

Смотрите заголовок: что означает встроенная функция C ++?

Ответы [ 9 ]

35 голосов
/ 01 октября 2008

Функция помещается в код, а не вызывается, подобно использованию макросов (концептуально)

Это может улучшить скорость (без вызова функции), но вызывает раздувание кода (если функция используется 100 раз, теперь у вас есть 100 копий)

Вы должны заметить, что это не заставляет компилятор делать функцию встроенной, и он будет игнорировать вас, если сочтет это плохой идеей. Точно так же компилятор может сделать для вас нормальные функции встроенными.

Это также позволяет вам поместить всю функцию в заголовочный файл, вместо того, чтобы реализовывать ее в файле cpp (который вы в любом случае не можете использовать, поскольку тогда вы получите неразрешенный внешний объект, если он был объявлен встроенным, если, конечно, только этот cpp файл использовал его).

26 голосов
/ 01 октября 2008

Это означает одно и только одно: компилятор исключит множество определений функции.

Функция обычно не может быть определена несколько раз (т.е. если вы поместите определение не встроенной функции в заголовок, а затем # включите его в несколько модулей компиляции, вы получите ошибку компоновщика). Пометка определения функции как «встроенного» устраняет эту ошибку (компоновщик гарантирует, что произойдет правильное).

ЭТО НЕ ЗНАЧИТ БОЛЬШЕ!

Самое главное, это НЕ означает, что компилятор будет встраивать скомпилированную функцию в каждый сайт вызова. То, происходит ли это, полностью зависит от прихотей компилятора, и обычно встроенный модификатор мало или ничего не меняет мнение компилятора. Компилятор может - и выполняет - встроенные функции, которые не помечены встроенными, и он может выполнять вызовы функций для функций, которые помечены встроенными.

Необходимо запомнить несколько определений.

20 голосов
/ 01 октября 2008

Как и другие (совершенно правильные) ответы о влиянии на производительность inline, в C ++ следует также отметить, что это позволяет безопасно помещать функцию в заголовок:

// my_thing.h
inline int do_my_thing(int a, int b) { return a + b; }

// use_my_thing.cpp
#include "my_thing.h"
...
    set_do_thing(&do_my_thing);

// use_my_thing_again.cpp
...
    set_other_do_thing(&do_my_thing);

Это связано с тем, что компилятор включает только фактическое тело функции в первый объектный файл, для которого требуется скомпилировать обычную вызываемую функцию (обычно потому, что ее адрес был взят, как я показал выше).

Без ключевого слова inline большинство компиляторов выдает ошибку о множественном определении, например для MSVC:

use_my_thing_again.obj : error LNK2005: "int __cdecl do_my_thing(int,int)" (?do_my_thing@@YAHHH@Z) already defined in use_my_thing.obj
<...>\Scratch.exe : fatal error LNK1169: one or more multiply defined symbols found
3 голосов
/ 02 октября 2008

@ OldMan

Компиляторы только встроенные, не помеченные ТОЛЬКО как встроенные функции, если вы запросите это.

Только если под «запросом» имеется в виду «включить оптимизации».

Это верно только в отношении причин.

Это верно в обоих случаях.

Inline не генерирует никакой дополнительной информации, которую может использовать компоновщик. Скомпилируйте 2 объектных файла и проверьте. Это позволяет использовать несколько определений, потому что символы не экспортируются! Не потому что это его цель!

Что вы имеете в виду, "символы не экспортируются"? встроенные функции не являются статичными. Их имена видны; у них есть внешняя связь. Цитировать из стандарта C ++:

void h (); встроенная пустота h (); // внешняя связь

встроенная пустота l (); пустота l (); // внешняя связь

Многочисленные определения очень важны. Обязательно:

Встроенная функция должна быть определена в каждой единице перевода, в которой она используется, и должна иметь точно одно и то же определение в каждом случае (3.2). [Примечание: вызов встроенной функции может встречаться до ее определения появляется в блоке перевода. ] Если функция с внешней связью объявлена ​​встроенной в одном переводе единица, она должна быть объявлена ​​встроенной во всех единицах перевода, в которых она появляется; Диагностика не требуется. встроенная функция с внешней связью должна иметь одинаковый адрес во всех единицах перевода.

2 голосов
/ 01 октября 2008

Тело функции буквально вставляется в функцию вызывающего. Таким образом, если у вас есть несколько вызовов этой функции, вы получите несколько копий кода. Преимущество в том, что вы получаете более быстрое исполнение.

Обычно очень короткие функции встроены, когда копия тела функции будет не намного больше, чем обычный код пролога / эпилога, сгенерированный для обычного вызова функции.

Вы можете прочитать больше в статье MSDN об inline - http://msdn.microsoft.com/en-us/library/z8y1yy88.aspx

1 голос
/ 01 октября 2008

Встроенные функции изменяют профиль производительности вашего приложения, возможно, генерируя инструкции, которые помещаются в сегмент кода вашего приложения. Будет ли функция встроена, остается на усмотрение вашего компилятора. По моему опыту, большинство современных компиляторов способны определить, когда следует выполнять запрос пользователя на включение.

Во многих случаях вставка функции улучшает ее производительность. Служебным вызовам присущи накладные расходы. Однако есть причины, по которым встраивание функции может быть отрицательным:

  • Увеличение размера исполняемого двоичного файла путем дублирования кода может привести к перегрузке диска и замедлению работы приложения.
  • Встраивание кода может привести к ошибкам в кеше или, возможно, к попаданиям в кеш в зависимости от вашей архитектуры.

FAQ по C ++ хорошо объясняет тонкости ключевого слова: http://www.parashift.com/c++-faq-lite/inline-functions.html#faq-9.3

0 голосов
/ 01 октября 2008

Функция, которую вы пометили как встроенную, может быть встроена компилятором. Нет гарантии, что compielr сделает это. Сам компилятор использует сложную семантику для устройства, когда это делать или нет.

Когда компилятор решает, что функция должна быть встроенной, вызов функции в коде вызывающей стороны заменяется кодом вызываемого кода. Это означает, что вы сохраняете операции стека, сам вызов и улучшаете локальность кэша кода. Иногда это может привести к огромному увеличению производительности. Специально для однострочных функций доступа к данным, таких как методы доступа, используемые в объектно-ориентированном коде.

Стоимость - это то, что обычно приводит к увеличению кода, что может снизить производительность. Вот почему установка функции для встроенного является всего лишь «зеленым флагом» для компилятора, которому не нужно следовать. Компилятор постарается сделать то, что лучше.

Как правило, для начинающих, которые не хотят иметь дело с особенностями связывания. встроенная функция должна вызываться другой функцией в тех же единицах компиляции. Если вы хотите реализовать встроенную функцию, которая может использоваться на нескольких блоках компиляции, сделайте это объявленным файлом заголовка и внедрите встроенную функцию.

Почему?

Пример: в заголовочном файле inlinetest.h

int foo();
inline int bar();

В модуле компиляции inlinetest.cpp

 int foo(){ int r = bar(); return r; }


 inline int bar(){ return 5;};

Затем на main.cpp

 #include "inlinetest.h"
 int main()
 {
  foo();
 //bar();
  }

Компиляция одного объектного файла за раз. Если вы раскомментируете этот вызов «bar», у вас будет ошибка. Потому что встроенная функция реализована только в объектном файле inlinetest.o и не экспортируется. В то же время функция foo, скорее всего, встраивает в нее код функции bar (так как bar является однострочной операцией ввода-вывода без операции ввода, то весьма вероятно, что она будет встроена в строку)

Но если в файле заголовка вы объявили встроенную функцию и внедрили ее в строку, то вы сможете использовать ее в любом модуле компиляции, который включает этот заголовок. («пример кода»);

Удалите ключевое слово inline, и компилятор НЕ вызовет ошибку даже при вызове bar в main. И никакого inline не произойдет, если вы не попросите компилятор встроить все функции. Это не стандартное поведение на большинстве компиляторов.

0 голосов
/ 01 октября 2008

Вызов функции налагает определенную потерю производительности для CPU по сравнению с просто линейным потоком инструкций. Регистры ЦП должны быть записаны в другое место и т. Д. Очевидно, что преимущества наличия функций обычно перевешивают снижение производительности. Но там, где производительность будет проблемой, например, легендарной функцией «внутреннего цикла» или каким-либо другим узким местом, компилятор может вставить машинный код для функции в основной поток выполнения вместо того, чтобы проходить налог ЦП за вызов функции. .

0 голосов
/ 01 октября 2008

Неофициально, это означает, что компиляторам разрешено перенести содержимое функции на сайт вызова, чтобы не было вызова функции. Если ваша функция имеет большие операторы управления (например, if, switch и т. Д.), И условия могут быть оценены во время компиляции на сайте вызова (например, постоянные значения, используемые на сайте вызова), то ваш код заканчивается намного меньше (неиспользуемые ветви опускаются).

Более формально, встроенные функции также имеют различную связь. Я позволю экспертам C ++ поговорить об этом аспекте.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...