#cpp #компоновщик
На этот вопрос уже даны ответы здесь:
В каком случае нужно определять функцию в заголовочном
файле?
(5 ответов)
Что такое “Правило одного определения” (One definition
rule)?
(1 ответ)
Закрыт 2 года назад.
Рассмотрим следующий пример:
// file header1.h
#include
void foo()
{
std::cout << "Foo" << '\n';
}
// file source1.cpp
#include "header1.h"
// file main.cpp
#include "header1.h"
int main()
{
return 0;
}
В связи с тем, что определение foo находится в хедере (что не очень хорошо), который
включён в более чем один .cpp файл, линкер честно выдает ошибку наподобие:
multiple definition of foo ... first defined in main.o ...
Одно из решений - сделать foo статической функцией, что обеспечит одно определение
в объектных файлах:
static foo() { ... }
второе:
inline foo() { ... }
третье:
static inline foo() { ... }
Вопросы:
Почему inline функция тоже решает эту проблему?
Какие принципиальные отличия у этих решений?
Ответы
Ответ 1
inline решает эту проблему потому, что спецификация языка этого требует. Правило Одного Определения (One Definition Rule) для inline функций ясно говорит, что inline функция с внешним связыванием может определяться в программе много раз в разных единицах трансляции (точнее: должна определяться везде, где одна используется и везде - одинаково). А уж как компиляторы/линкеры реализуют это требование - это их проблемы. Фактически, если такая функция породила тело в нескольких объектных файлах (т.е. не все ее вызовы заинлайнились), то линкеру будет предписано не ругаться на тему "multiple definition", а молча выбрать одно-единственное тело и остальные выбросить. Так как "классический" формат объектного файла не позволяет определить, где начинается и где заканчивается тело конкретной функции, для целей возможного будущего удаления компилятор их обычно помещает в выделенные секции объектного файла. Принципиальное отличие между inline и static вариантом заключается в том, что inline функция сохраняет внешнее связывание (external linkage) и ведет себя во всех отношениях как функция со внешним связыванием. Например, если разные единицы трансляции попробуют взять адрес inline функции, то везде обязательно должно получиться одно и то же значение. Статические переменные, определенные внутри такой inline функции тоже существуют в единственном экземпляре во всей программе, как и в любой другой функции со внешним связыванием. В static варианте наоборот, функция получает внутреннее связывание (internal linkage) - каждая единица трансляции получает свою копию функции со своим уникальным адресом. Принципиальных отличий между static inline и static нет, с той только разницей, что в первом случае дается подсказка компилятору.Ответ 2
это очевидно из самой сути inline -- если код такой функции в самом деле "заинлайнен", то на него существует (логически) только одна ссылка (а именно в том месте, где код "инлайнится"), следовательно никаких проблем с "multiple definition" (т.е. проблемы выбора той или иной точки входа) не существует. тут принципиальное отличие возникает при наличии static переменных у таких (inline и static inline функций. В каждой единице компиляции у static inline будут свои экземпляры таких переменных (а у "не static" один экземпляр static переменной на весь загрузочный модуль (результат линковки)).
Комментариев нет:
Отправить комментарий