Когда я должен написать ключевое слово «inline» для функции / метода?

403

Когда следует писать ключевое слово inline для функции/метода в С++?

После просмотра некоторых ответов некоторые связанные вопросы:

  • Когда я должен не писать ключевое слово 'inline' для функции/метода в С++?

  • Когда компилятор не знает, когда нужно сделать функцию/метод 'inline'?

  • Имеет ли значение, если приложение многопоточно, когда вы пишете 'inline' для функции/метода?

  • 34
    Если вы определяете функцию в заголовке, вам нужно объявить ее встроенной. В противном случае вы получите ошибки компоновщика о нескольких определениях функции.
  • 14
    @Martin: Если это не в определении класса, чтобы быть разборчивым.
Показать ещё 2 комментария
Теги:
inline
one-definition-rule

12 ответов

679
Лучший ответ

О человек, один из моих питомцев.

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.

  • 142
    +1 Лучшее описание inline, которое я видел в ... (навсегда). Теперь я оторву вас и буду использовать это во всех моих объяснениях встроенного ключевого слова.
  • 1
    Значит ли ваш ответ на вопрос «Когда я не должен писать ...» по сути дела «если вы думаете, что функция должна быть встроенной, то она не должна». Вы говорите, что мы должны, как правило, включать один вкладыш и не слишком задумываться об остальном?
Показать ещё 23 комментария
26

Вам все равно нужно явно встраивать свою функцию при выполнении специализированной специализации (если специализация находится в файле .h)

25

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

Для двух исходных файлов, таких как:

  • 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.
  • 3
    Ваш ответ является иллюстративным сообщением о том, почему язык делает такие inline функции неопределенным поведением.
17

1) В наше время почти никогда. Если это хорошая идея встроить функцию, компилятор сделает это без вашей помощи.

2) Всегда. См. № 1.

(Отредактировано с учетом того, что вы нарушили свой вопрос на два вопроса...)

  • 0
    Да. Встроенный является лишь подсказкой компилятору, и он может игнорировать вас. В наши дни компилятор, вероятно, знает лучше, чем программист, какие функции лучше всего встроить.
  • 0
    Это верно и для C, верно?
Показать ещё 6 комментариев
8

Когда я не должен писать ключевое слово 'inline' для функции/метода в С++?

Если функция определена в файле .cpp, вы не должны писать ключевое слово.

Когда компилятор не знает, когда нужно сделать функцию/метод 'inline'?

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

Имеет ли значение, если приложение многопоточно, когда вы пишете 'inline' для функции/метода?

Нет, это вообще не имеет значения.

4
  • Когда компилятор не знает, когда нужно сделать функцию/метод 'inline'?

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

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

(Более подробно: математическое моделирование с несколькими критическими функциями, определенными вне класса, GCC 4.6.3 (g++ -O3), ICC 13.1.0 (icpc-O3), добавление строки в критические точки вызвало + 6% ускорение с кодом GCC).

Итак, если вы квалифицируете GCC 4.6 как современный компилятор, результатом является то, что директива inline все еще имеет значение, если вы пишете интенсивные задачи с ЦП и знаете, где именно находится узкое место.

  • 4
    Я хотел бы видеть больше доказательств в поддержку ваших утверждений. Пожалуйста, предоставьте код, который вы тестируете, а также вывод ассемблера с встроенным ключевым словом и без него. Любое количество вещей могло бы дать вам выигрыш в производительности.
  • 1
    Наконец, кто-то, кто не только повторяет то, что говорят другие, но на самом деле проверяет эти утверждения. Gcc действительно все еще рассматривает ключевое слово inline как подсказку (я думаю, что clang полностью его игнорирует).
Показать ещё 1 комментарий
4

В действительности, почти никогда. Все, что вы делаете, означает, что компилятор делает заданную функцию встроенной (например, заменяет все вызовы на эту функцию /w ее тело). Конечно, нет никаких гарантий: компилятор может игнорировать директиву.

Компилятор обычно хорошо справляется с обнаружением + оптимизации таких вещей.

  • 7
    Проблема в том, что inline имеет семантическое различие в C ++ (например, в способе обработки нескольких определений), что важно в некоторых случаях (например, шаблоны).
  • 4
    Встроенный используется для разрешения случаев, когда символ имеет несколько определений. Тем не менее, шаблоны уже обрабатываются языком. Единственным исключением является специализированная функция шаблона, которая больше не имеет никаких параметров шаблона (template <>). Они рассматриваются как функции, а не как шаблоны, поэтому для связывания необходимо ключевое слово inline.
2

gcc по умолчанию не выполняет никаких функций при компиляции без оптимизация включена. Я не знаю о visual studio - deft_code

Я проверил это для Visual Studio 9 (15.00.30729.01), скомпилировав /FAcs и посмотрев на код сборки: Компилятор создал вызовы функций-членов без оптимизации в режиме отладки. Даже если функция отмечена __ forceinline, не создается встроенный код времени выполнения.

  • 1
    Включить / Wall, чтобы узнать о том, какие функции были помечены как встроенные, но не были встроены
0

При разработке и отладке кода оставьте inline вне. Это усложняет отладку.

Основная причина их добавления - помочь оптимизировать сгенерированный код. Обычно эта торговля увеличивает пространство кода для скорости, но иногда inline сохраняет как пространство, так и время выполнения.

Рассекая эту мысль об оптимизации производительности до завершения алгоритма преждевременная оптимизация.

  • 12
    inline функции обычно не встроены, если не компилируются с оптимизацией, поэтому они никак не влияют на отладку. Помните, что это подсказка, а не требование.
  • 3
    gcc по умолчанию не включает никаких функций при компиляции без включенной оптимизации. Я не знаю о визуальной студии
Показать ещё 3 комментария
0

Вы хотите поместить его в самом начале, перед типом возврата. Но большинство компиляторов игнорируют это. Если он определен и имеет меньший блок кода, большинство компиляторов все равно считают его inline.

-2

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

(Но см. Есть ли причина, почему бы не использовать оптимизацию времени ссылки?

-2

Когда нужно встроить:

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

2. Функция должна быть маленькой, часто вызываемой и сделать встроенную, действительно выгодной, поскольку в соответствии с правилом 80-20 попытайтесь сделать эту функцию встроенной, что существенно повлияет на производительность программы.

Как мы знаем, inline - это просто запрос к компилятору, похожий на регистр, и он будет стоить вам при размере Object-кода.

  • 0
    «inline - это просто запрос к компилятору, похожий на регистр». Они похожи, потому что ни запросы, ни какое-либо отношение к оптимизации не имеют. inline утратил свой статус подсказки по оптимизации, и большинство компиляторов используют его только для учета нескольких определений, как это должно быть в IMO. Более того, начиная с C ++ 11, register полностью устарела из-за своего прежнего значения «я знаю лучше, чем компилятор, как оптимизировать»: теперь это просто зарезервированное слово без текущего значения.
  • 0
    @underscore_d: Gcc все еще слушает inline в некоторой степени.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню