Подтвердить что ты не робот

Есть ли еще возможность для встроенного?

Я полагал, что inline устарел, потому что я читал здесь:

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

Однако Angew, похоже, что-то понимает. В этом вопросе он и я довольно часто возвращаемся туда и обратно, о том, насколько полезен inline.

Этот вопрос не является вопросом:

Принимая во внимание, что компилятор может inline по желанию, поэтому inline здесь не помогает: Где можно использовать inline, чтобы заставить, а не предлагать, изменение скомпилированного кода?

4b9b3361

Ответ 1

Я попытаюсь объяснить свое "секретное понимание" наилучшим образом, как только смогу.

Здесь есть две совершенно разные концепции. Одним из них является способность компилятора заменить вызов функции, повторяя тело функции непосредственно на сайте вызова. Другая - возможность определения функции в нескольких единицах перевода (= более одного файла .cpp).

Первая называется функцией inline. Вторая цель - ключевое слово 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, когда оно не имеет ничего общего с вложением? Причина проста: встроить функцию (то есть заменить вызов на нее, повторив ее тело на сайте вызова), компилятор должен иметь тело функции в первую очередь.

С++ следует за отдельной моделью компиляции, где компилятор не имеет доступа к объектным файлам, отличным от того, который он в настоящее время производит. Поэтому, чтобы иметь возможность встроить функцию, ее определение должно быть частью текущей единицы перевода. Если вы хотите иметь возможность встроить его в несколько единиц перевода, его определение должно быть во всех них. Как правило, это приведет к ошибке множественного определения. Поэтому, если вы помещаете свою функцию в заголовок и #include ее определение везде, чтобы включить ее встраивание повсюду, вы должны пометить ее как inline, чтобы предотвратить множественные ошибки определения.

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

Компилятор не может. Линкером может быть. Современные методы оптимизации включают в себя создание кода времени выполнения (оптимизация всей программы.), Где оптимизатор запускается поверх всех объектных файлов как часть процесса связывания до фактической привязки. На этом этапе все функциональные определения, конечно, доступны, и вложение вполне возможно без использования одного ключевого слова inline в любом месте программы. Но эта оптимизация, как правило, требует больших затрат времени, особенно для крупных проектов. Имея это в виду, полагаться только на LTCG для inlining, возможно, не лучший вариант.


Для полноты: я слегка обманул в первой части. Свойство ODR на самом деле не является свойством ключевого слова inline, а встроенных функций (которое является термином языка). Правила для встроенных функций:

  • Может быть определен в нескольких единицах перевода без возникновения ошибок компоновщика.
  • Должен быть определен в каждой единицы перевода, в которой он используется.
  • Все его определения должны быть токенами для-токенов и идентичными сущностями для объекта

Ключевое слово inline превращает функцию в встроенную функцию. Другой способ отметить функцию как встроенную - определить (а не просто объявить) ее непосредственно в определении класса. Такая функция встроена автоматически, даже без ключевого слова inline.

Ответ 2

inline теперь в основном только внешний указатель привязки по причинам, которые вы указали.

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

//header.h
inline void foo() {}
void goo() {}

//cpp1.cpp
#include "header.h"

//cpp2.cpp
#include "header.h"

// foo is okay, goo breaks the one definition rule (ODR)

Фактически принудительная вставка функции зависит от компилятора, некоторые могут иметь поддержку через определенные attribute или pragma или (__forceinline) или еще что-то.

Проще говоря, он позволяет определять функции в заголовках без нарушения ODR...