Что означает встроенная функция C++?

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

9 ответов

Решение

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

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

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

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

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

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

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

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

Следует помнить об исключении нескольких определений.

А также другие (совершенно правильные) ответы о влиянии на производительность 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

@Пожилой человек

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

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

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

Это правильно в обоих.

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

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

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

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

Множество определений - очень большая цель. Это обязательно:

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

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

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

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

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

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

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

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

Функция, которую вы помечаете как встроенную, может быть встроена компилятором. Нет никаких гарантий, что 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 не произойдет, если вы не попросите компилятор встроить все функции. Это не стандартное поведение на большинстве компиляторов.

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

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

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

Другие вопросы по тегам