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

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

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

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

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

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

Ответ 1

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

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.

Ответ 2

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

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

  • 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

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

Ответ 4

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

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

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

Ответ 5

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

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

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

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

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

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

Ответ 6

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

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

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

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

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

Ответ 7

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

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

Ответ 8

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

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

Ответ 9

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

Ответ 10

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

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

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

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

Ниже приведен пример, который использует встроенную функцию для возврата максимум двух чисел

#include <iostream>

using namespace std;

inline int Max(int x, int y) {
   return (x > y)? x : y;
}

// Main function for the program
int main() {
   cout << "Max (20,10): " << Max(20,10) << endl;
   cout << "Max (0,200): " << Max(0,200) << endl;
   cout << "Max (100,1010): " << Max(100,1010) << endl;

   return 0;
}

Для получения дополнительной информации см. здесь.

Ответ 11

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

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

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

Ответ 12

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

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

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

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

Ответ 13

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

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