Я постараюсь как можно лучше объяснить свое «тайное понимание».
Здесь есть два совершенно разных понятия. Один из них - это способность компилятора заменять вызов функции, повторяя тело функции непосредственно в месте вызова. Другой - возможность определения функции более чем в одной единице перевода (= более чем в одном .cpp
файле).
Первый называется встраиванием функций. Второй - это назначение ключевого слова inline
. Исторически ключевое слово inline
также убедительно предлагало компилятору встроить функцию, помеченную inline
. Поскольку компиляторы стали лучше оптимизировать, эта функциональность уменьшилась, и использование inline
в качестве предложения встроить функцию действительно устарело. Компилятор с радостью проигнорирует это и встроит что-то еще, если сочтет, что это лучшая оптимизация.
Надеюсь, мы разобрались с явным отношением inline
inlining. В текущем коде его нет.
Итак, какова реальная цель ключевого слова inline
? Все просто: функция с пометкой inline
может быть определена в нескольких единицах перевода без нарушения правила единого определения (ODR). Представьте себе эти два файла:
file1.cpp
int f() { return 42; }
int main()
{ return f(); }
file2.cpp
int f() { return 42; }
Эта команда:
> gcc file1.cpp file2.cpp
Выдает ошибку компоновщика с жалобой на то, что символ f
определен дважды.
Однако, если вы помечаете функцию ключевым словом inline
, она конкретно сообщает компилятору и компоновщику: «Ребята, убедитесь, что несколько идентичных определений этой функции не приводят к каким-либо ошибкам!»
Таким образом, будет работать следующее:
file1.cpp
inline int f() { return 42; }
int main()
{ return f(); }
file2.cpp
inline int f() { return 42; }
Компиляция и связывание этих двух файлов вместе не приведет к ошибкам компоновщика.
Обратите внимание, что определение f
не обязательно должно быть дословно в файлах. Вместо этого он может поступать из файла заголовка #include
d:
f.hpp
inline int f() { return 42; }
file1.cpp
#include "f.hpp"
int main()
{ return f(); }
file2.cpp
#include "f.hpp"
По сути, чтобы иметь возможность записать определение функции в файл заголовка, вы должны пометить его как inline
, иначе это приведет к множественным ошибкам определения.
Последний кусок головоломки: почему ключевое слово на самом деле пишется inline
, когда оно не имеет никакого отношения к встраиванию? Причина проста: чтобы встроить функцию (то есть заменить ее вызов повторением ее тела на сайте вызова), компилятор должен иметь тело функции в первую очередь.
C ++ следует отдельной модели компиляции, где компилятор не имеет доступа к объектным файлам, кроме тех, которые он в настоящее время создает. Следовательно, чтобы иметь возможность встроить функцию, ее определение должно быть частью текущей единицы перевода. Если вы хотите иметь возможность встроить его в несколько единиц перевода, его определение должно быть во всех из них. Обычно это приводит к множественной ошибке определения. Поэтому, если вы поместите свою функцию в заголовок и #include
ее определение повсюду, чтобы включить ее встраивание повсюду, вы должны пометить ее как inline
, чтобы предотвратить множественные ошибки определения.
Обратите внимание, что даже сегодня, хотя компилятор будет встроить любую функцию, которую он считает нужной, он все еще должен иметь доступ к определению этой функции. Таким образом, хотя ключевое слово inline
не требуется в качестве подсказки «пожалуйста, вставьте это», вы все равно можете обнаружить, что вам нужно использовать его, чтобы включить компилятор для выполнения встраивания, если он решит это сделать. Без него вы не сможете получить определение в единицу перевода, а без определения компилятор просто не сможет встроить функцию.
Компилятор не может. Компоновщик может. Современные методы оптимизации включают генерацию кода во время компоновки (также известную как оптимизация всей программы), при которой оптимизатор запускается для всех объектных файлов как часть процесса связывания перед фактическим связыванием. На этом этапе, конечно, доступны все определения функций, и встраивание вполне возможно без использования единственного ключевого слова inline
где-либо в программе. Но такая оптимизация обычно требует больших затрат времени на сборку, особенно для крупных проектов. Имея это в виду, полагаться только на LTCG для встраивания может быть не лучшим вариантом.
Для полноты: в первой части я немного схитрил. Свойство ODR на самом деле не является свойством ключевого слова inline
, а является свойством встроенных функций (что является термином языка). Правила для встроенных функций:
- Может быть определен в нескольких единицах перевода, не вызывая ошибок компоновщика
- Должен быть определен в каждой единице перевода, в которой он используется
- Все его определения должны быть идентичными для токена для токена и сущности для сущности.
Ключевое слово inline
превращает функцию во встроенную функцию. Другой способ пометить функцию как встроенную - определить (а не просто объявить) ее непосредственно в определении класса. Такая функция является встроенной автоматически, даже без ключевого слова inline
.
person
Angew is no longer proud of SO
schedule
22.04.2015
inline
на встраиваемый код. Я специально заявляю, что не хотел об этом знать. Я хотел узнать об альтернативном использованииinline
s. На мой взгляд, эти два вопроса очень разные. - person Jonathan Mee   schedule 22.04.2015inline
бесполезно намекать на встраивание, остается только семантика, и это именно то, что. Когда мне следует писать ключевое слово «inline» для функции / метода? было около. Но поскольку ваш вопрос был повторно открыт, по-видимому, другие думали так же. Обратите внимание: если вы просмотрели другие вопросы, рекомендуется связать их и объяснить, почему вы думаете, что ваш вопрос отличается. - person Matthieu M.   schedule 23.04.2015inline
можно использовать для принудительного изменения скомпилированного кода?, На который нет ответа, если вы не попадете в некоторые специализированные параметры компилятора. Затем вы говорите, что на самом деле вы все время знали первое, а настоящий вопрос - это второе. - person Steve Jessop   schedule 23.04.2015inline
можно использовать для принудительного изменения скомпилированного кода; в этом код не может быть скомпилирован безinline
из-за ODR. Я размышлял, как мне повысить точность ответа на этот вопрос, но в конце концов это показалось мне лучшим, что я уже мог сделать. Я приветствовал бы предложение, если вы чувствуете, что можно сказать что-то, что уместно дифференцирует вопрос. - person Jonathan Mee   schedule 23.04.2015