NB. Это не вопрос о том, как использовать встроенные функции или как они работают, а скорее о том, почему они сделаны такими, какие есть.
Объявление функции-члена класса не требует определения функции, поскольку inline
это только фактическая реализация функции. Например, в заголовочном файле:
struct foo{
void bar(); // no need to define this as inline
}
Так почему же рядная реализация функциональных классов должна быть в заголовочном файле? Почему я не могу поместить в .cpp
файл встроенную функцию ? Если бы я попытался поместить встроенное определение в .cpp
файл, я бы получил сообщение об ошибке в следующих строках:
error LNK2019: unresolved external symbol
"public: void __thiscall foo::bar(void)"
(?bar@foo@@QAEXXZ) referenced in function _main
1>C:\Users\Me\Documents\Visual Studio 2012\Projects\inline\Debug\inline.exe
: fatal error LNK1120: 1 unresolved externals
inline
появляется в определении, но не в предыдущем объявлении, и наоборот . Если да, то это может помочь: stackoverflow.com/questions/4924912/…Ответы:
Определение
inline
функции не обязательно должно быть в файле заголовка, но из-за единого правила определения ( ODR ) для встроенных функций идентичное определение функции должно существовать в каждой единице перевода, которая ее использует.Самый простой способ добиться этого - поместить определение в файл заголовка.
Если вы хотите поместить определение функции в один исходный файл, вам не следует объявлять его
inline
. Отсутствие объявления функции неinline
означает, что компилятор не может встроить функцию.Следует ли вам объявлять функцию
inline
или нет - это обычно выбор, который вы должны сделать в зависимости от того, какую версию одного правила определения вам имеет наибольший смысл следовать; добавление,inline
а затем ограничение последующими ограничениями не имеет смысла.источник
+1
от меня!На это можно взглянуть двумя способами:
Встроенные функции определены в заголовке, потому что для встраивания вызова функции компилятор должен иметь возможность видеть тело функции. Чтобы наивный компилятор мог это сделать, тело функции должно находиться в той же единице трансляции, что и вызов. (Современный компилятор может оптимизировать единицы трансляции, поэтому вызов функции может быть встроен, даже если определение функции находится в отдельной единице трансляции, но эти оптимизации дороги, не всегда включены и не всегда поддерживаются компилятор)
функции, определенные в заголовке, должны быть отмечены,
inline
потому что в противном случае каждая единица трансляции, которая включает заголовок, будет содержать определение функции, и компоновщик будет жаловаться на несколько определений (нарушение правила одного определения).inline
Ключевое слово подавляет это, позволяя несколько единиц перевода , чтобы содержать (одинаковое) определение.Два объяснения на самом деле сводятся к тому, что
inline
ключевое слово не совсем то, что вы ожидаете.Компилятор C ++ может применять оптимизацию встраивания (заменять вызов функции телом вызываемой функции, сохраняя накладные расходы на вызов) в любое время, когда он хочет, если это не изменяет наблюдаемое поведение программы.
inline
Ключевые слова делают его проще для компилятора , чтобы применить эту оптимизацию, позволяя определение функции , чтобы быть видимым в нескольких единицах трансляции, но с использованием ключевого слова не означает , что компилятор имеет встраивать функцию, а не с помощью ключевого слова не запретить компилятору встраивать функцию.источник
Это предел компилятора C ++. Если вы поместите функцию в заголовок, все файлы cpp, в которые она может быть встроена, смогут увидеть «источник» вашей функции, а встраивание может быть выполнено компилятором. В противном случае встраивание должно выполняться компоновщиком (каждый файл cpp компилируется в файл obj отдельно). Проблема в том, что сделать это в компоновщике будет намного сложнее. Аналогичная проблема существует с "шаблонными" классами / функциями. Они должны быть созданы компилятором, потому что у компоновщика возникнут проблемы с их экземпляром (созданием специальной версии). Некоторые более новые компиляторы / компоновщики могут выполнять "двухпроходную" компиляцию / компоновку, когда компилятор выполняет первый проход, затем компоновщик выполняет свою работу и вызывает компилятор для решения неразрешенных вещей (встроенные / шаблоны ...)
источник
Причина в том, что компилятор должен фактически видеть определение , чтобы иметь возможность вставить его вместо вызова.
Помните, что C и C ++ используют очень упрощенную модель компиляции, когда компилятор всегда видит только одну единицу перевода за раз. (Это не удается для экспорта, что является основной причиной, по которой только один поставщик фактически реализовал это.)
источник
inline
Ключевое слово c ++ вводит в заблуждение, оно не означает «встроить эту функцию». Если функция определена как встроенная, это просто означает, что ее можно определять несколько раз, если все определения равны. Совершенно законно для функции, помеченнойinline
как реальная функция, которая вызывается вместо того, чтобы получить код, встроенный в точку, где она вызывается.Определение функции в файле заголовка необходимо для шаблонов, поскольку, например, шаблонный класс на самом деле не является классом, это шаблон для класса, для которого вы можете сделать несколько вариантов. Чтобы компилятор мог, например, создать
Foo<int>::bar()
функцию, когда вы используете шаблон Foo для создания класса Foo , фактическое определениеFoo<T>::bar()
должно быть видимым.источник
inline
(и не декларирование этогоinline
гарантирует, что она не будет встроена).Я знаю, что это старая ветка, но подумал, что стоит упомянуть
extern
ключевое слово. Недавно я столкнулся с этой проблемой и решил следующим образомhelper.h
Helper.cpp
источник
Потому что компилятор должен их видеть, чтобы встроить их. А файлы заголовков - это «компоненты», которые обычно включаются в другие единицы перевода.
источник
Встроенные функции
В C ++ макрос - это не что иное, как встроенная функция. Итак, теперь макросы находятся под управлением компилятора.
Код встроенной функции заменяется в том месте, где она вызывается, что снижает накладные расходы на вызов функции.
В некоторых случаях встраивание функции не может работать, например
Если статическая переменная используется внутри встроенной функции.
Если функция сложная.
Если рекурсивный вызов функции
Если адрес функции взят неявно или явно
Функция, определенная вне класса, как показано ниже, может стать встроенной
Функция, определенная внутри класса, также становится встроенной
Здесь обе функции getSpeed и setSpeed станут встроенными
источник