Когда мне следует написать ключевое слово «inline» для функции / метода? - PullRequest
494 голосов
/ 19 ноября 2009

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

Увидев некоторые ответы, некоторые связанные вопросы:

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

  • Когда компилятор не будет знать, когда сделать функцию / метод «встроенным»?

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

Ответы [ 13 ]

781 голосов
/ 19 ноября 2009

О, чувак, одна из моих любимых мозолей.

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

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

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

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

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

Примечание: Как правило, объявление шаблонов inline бессмысленно, так каку них уже есть семантика связывания inline.Однако для явной специализации и создания шаблонов необходимо использовать inline.


Конкретные ответы на ваши вопросы:

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

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

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

    Don 'не добавляйте inline только потому, что вы думаете, что ваш код будет работать быстрее, если компилятор его встроит.

  • Когда компилятор не будет знать, когда сделать функцию / метод «встроенным»?

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

    В качестве исключения для предотвращения встраивания в GCC, используйте __attribute__(( noinline )), а в Visual Studio - __declspec(noinline).

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

    Многопоточность не влияет на встраиваниелюбым способом.

45 голосов
/ 16 августа 2017

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

Имеются два исходных файла, например:

  • 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
    

    Обсуждение

    1. Даже если вы должны иметь идентичные определения вашего inline функции, компилятор C ++ не помечает его, если это не так (на самом деле, из-за отдельной компиляции он не может проверить это). Это ваша личная обязанность обеспечить это!

    2. Линкер не жалуется на Одно правило определения , так как fun() объявлено как inline. Однако, поскольку inline111.cpp является первым модулем преобразования (который фактически вызывает fun()), обработанным компилятором, компилятор создает экземпляр fun() при его первом обращении к вызову в inline111.cpp . Если компилятор решит , а не расширить fun() при его вызове из любого другого места в вашей программе ( например из inline222.cpp ), вызов fun() всегда будет связан с его экземпляром, созданным из inline111.cpp (вызов fun() внутри inline222.cpp может также привести к созданию экземпляра в этой единице перевода, но он останется несвязанным ). Действительно, это видно из идентичных &fun = 0x4029a0 распечаток.

    3. Наконец, несмотря на то, что inline предложение компилятору фактически расширяет однострочник fun(), оно полностью игнорирует ваше предложение, что очевидно, потому что fun() = 111 в обеих строках.


  • Дело B:

    Компиляция (уведомление в обратном порядке) :

    g++ -std=c++11 inline222.cpp inline111.cpp
    

    выход

    inline111: fun() = 222, &fun = 0x402980
    inline222: fun() = 222, &fun = 0x402980
    

    Обсуждение

    1. Этот случай подтверждает то, что обсуждалось в Случай A .

    2. Обратите внимание на важный момент: если вы закомментируете фактический вызов fun() in inline222.cpp ( например comment out cout -statement in inline222.cpp полностью) тогда, несмотря на порядок компиляции ваших единиц перевода, fun() будет создан при первой встрече с вызовом в inline111.cpp , что приведет к распечатке для Дело 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
    

    Обсуждение

    1. Как описано здесь * Оптимизация , -O2 побуждает компилятор фактически расширять функции, которые могут быть встроены (Обратите также внимание, что -fno-inline является по умолчанию без вариантов оптимизации). Как видно из приведенного здесь фрагмента, fun() на самом деле был расширен (в соответствии с его определением в этой конкретной единице перевода), в результате чего два fun() распечатки. Несмотря на это, по-прежнему имеется только один глобально связанный экземпляр fun() (как того требует стандарт), как видно из идентичной &fun распечатки.
27 голосов
/ 19 ноября 2009

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

18 голосов
/ 19 ноября 2009

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

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

(отредактировано, чтобы отразить, что вы разбили свой вопрос на два вопроса ...)

11 голосов
/ 17 января 2014

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

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

Когда компилятор не будет знать, когда сделать функцию / метод «встроенным»?

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

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

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

5 голосов
/ 16 марта 2013
  • Когда компилятор не будет знать, когда сделать функцию / метод «встроенным»?

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

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

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

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

3 голосов
/ 19 ноября 2009

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

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

2 голосов
/ 17 июля 2012

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

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

0 голосов
/ 03 мая 2019

Встроенная функция 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 (100,1010): " << Max(100,1010) << endl;

   return 0;
}

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

0 голосов
/ 29 августа 2016

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

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

...