2009-11-18 5 views
376

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

Осмотрев некоторые ответы, некоторые смежные вопросы:

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

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

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

+32

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

+13

@Martin: Если это не в определении класса, быть разборчивым. –

+11

@David: быть лишним, это связано только с тем, что такие функции неявно отмечены 'inline' (9.3/2). –

ответ

619

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

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

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

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

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

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

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


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

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

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

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

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

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

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

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

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

    Многопоточность не влияет ни на что.

+111

+1 Лучшее описание inline Я видел в ... (навсегда). Теперь я разорву вас и использую это во всех моих объяснениях ключевого слова inline. –

+1

Ваш ответ на вопрос «Когда я не должен писать ...» по существу означает «если вы считаете, что функция должна быть встроенной, она не должна». Вы говорите, что мы должны, как правило, встраивать один лайнер и не думать слишком много об остальном? – Ziggy

+6

@ Ziggy, то, что я пытался сказать, было то, что инжиниринг компилятора и ключевое слово 'inline' не связаны. Однако у вас есть правильная идея. Как правило, угадать, что было бы улучшено путем inline, очень подвержено ошибкам. Исключение из этого правила - это один лайнер. –

17

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

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

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

+0

Да. Встроенная строка является лишь подсказкой для компилятора, и вы можете игнорировать вас. В наши дни компилятор, вероятно, знает лучше, чем программист, функции которого лучше всего встроены. –

+0

Это верно и для C, правильно? –

+1

Да, но это менее актуально - для функции, которая должна быть встроена, ее тело должно находиться в одном модуле компиляции (например, в заголовке). Это менее распространено в программах на C. –

4

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

Компилятор, как правило, хорошо разбирается в + оптимизации таких вещей.

+6

Проблема заключается в том, что 'inline' имеет разницу _semantic_ в C++ (например, в способе обработки нескольких определений), что важно в некоторых случаях (например, шаблоны). –

+4

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

0

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

-1

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

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

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

+11

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

+3

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

+0

Я работал над огромным проектом g ++, в котором была включена отладка. Возможно, другие варианты не позволили, но встроенные функции были встроены. В них невозможно было установить в них осмысленную точку останова. – wallyk

25

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

-1

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

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

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

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

+0

«inline - это просто запрос к компилятору, подобный регистру». Они похожи, потому что ни запросы, ни какие-либо связанные с оптимизацией. 'inline' потерял свой статус подсказки оптимизации, и большинство компиляторов используют его только для того, чтобы сделать допущения для нескольких определений - как ИМО, которым они должны. Более того, поскольку C++ 11 «register» полностью устарел из-за своего прежнего значения «Я знаю лучше, чем компилятор, как оптимизировать»: теперь это просто зарезервированное слово без текущего значения. –

+0

@underscore_d: Gcc до сих пор слушает 'inline' в некоторой степени. – MikeMB

2

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

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

+1

Включить/Стена, чтобы узнать, какие функции отмечены как встроенные, но на самом деле не вступил в число – paulm

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

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

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

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

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

+2

Я бы хотел увидеть больше доказательств, подтверждающих ваши претензии. Просьба представить код, который вы тестируете, а также вывод ассемблера с ключевым словом и без него.Любое количество вещей могло бы дать вам преимущества в производительности. –

+0

Наконец-то тот, кто не только повторяет то, что говорят другие, но действительно проверяет эти утверждения. Gcc действительно все еще считает ключевое слово inline в качестве подсказки (я думаю, что clang полностью игнорирует его). – MikeMB

+0

@ void.pointer: Почему так трудно поверить? Если оптимизаторы были уже совершенны, то новые версии не смогли улучшить производительность программы. Но они регулярно делают. – MikeMB

8

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

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

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

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

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

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

-1

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

(Но см Is there any reason why not to use link time optimization?)

11

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

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

  • 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. Даже ты вы должны иметь одинаковые определения ваших функций рядный , C++ компилятор не флаг, если это не так (на самом деле, из-за раздельной компиляции он не имеет возможности для проверки). Это ваша обязанность обеспечить это!

    2. Linker не жалуются на одно определение Правило как 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. Данный случай утверждает, что обсуждались в Case А.

    2. Обратите внимание важный момент, что если вы закомментировать фактический вызов fun() в inline222.cpp (например закомментируйте cout -statement в inline222.cpp полностью), то, несмотря на приказ компиляции ваши единицы перевода, fun() будут созданы при первом вызове вызова в inline111.cpp, что приведет к распечатке для Case B как inline111: fun() = 111, &fun = 0x402980.


  • Случай C:

    Compile(уведомление -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. Как described here, -O2 оптимизация поощряет компилятор фактически расширить функции, которые могут быть встраиваемыми (N Кроме того, -fno-inline является по умолчанию без вариантов оптимизации). Как видно из outprint здесь, то fun() фактически был рядный расширен (в соответствии с ее определением в этом частности ЕП), в результате чего два различныхfun() распечаток. Несмотря на это, есть еще только один экземпляр, связанный глобально связанный с fun() (как требуется стандартом), как видно из идентичный&fun распечатка.
Смежные вопросы