Определяется несколько раз для встроенной функции, насколько это возможно? - PullRequest
0 голосов
/ 29 ноября 2018

Следующая цитата из учебника по c ++ меня сильно смущает

В отличие от других функций, inline и constexpr-функции могут быть определены в программе несколько раз.В конце концов, для расширения кода компилятору нужно определение, а не просто объявление.Тем не менее, все определения данного inline или constexpr должны точно совпадать.В результате функции inline и constexpr обычно определяются в заголовках.- C ++ primer 5th Ed, 240 pp

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

Может ли кто-нибудь дать мне пример, почему существует множественное определение.

Ответы [ 3 ]

0 голосов
/ 29 ноября 2018

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

Но это не так, как видят компилятор и компоновщикмир.Если у вас есть встроенная функция foo в заголовочном файле, которая вызывается из a.cpp и b.cpp, то полная скомпилированная версия этой функции будет включена в a.obj и b.obj.Компоновщик решает проблему, выбирая только одну из этих скомпилированных версий для включения в ваш окончательный двоичный файл.

Обратите внимание, что здесь я приукрашиваю важные детали, но это общая идея (и, как мне кажется, ваш учебникускользает от).

0 голосов
/ 29 ноября 2018

В качестве примера.Эта версия недействительна.

// main.cpp
inline int square(int num) {
    return num * num;
}

inline int square(int num) {
    return num * num;
}

int main()
{
    return square(2);
}

https://godbolt.org/z/nlSbxg

Но если она у вас есть в нескольких .cpp файлах (или переводных единицах), это нормально, потому что теперь это компоновщикработа, чтобы делать правильные вещи.

// b.cpp
inline int square(int num) {
    return num * num;
}

// main.cpp
inline int square(int num) {
    return num * num;
}

int main()
{
    return square(2);
}

Сборка: gcc main.cpp b.cpp Точно так же работает #include он поместит код в эти .cpp файлы, и все.

Конечно, если телофункция встроенная , тогда ничего связывать, так что нет проблем:)

Если компилятор решит сделать версию вне линии, вы получите более одного объектного файла (.o) имея определение для той же самой "встроенной" функции.Такое определение будет помечено.

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

Так что, если все определения действительно одинаковытогда хорошо!Мы попадем в неприятности, если у нас будет другое тело такой функции.Пример в файле b.cpp

// b.cpp
inline int square(int num) {
    return 1;
}

Неопределенное поведение - иметь несколько разных определений одной и той же встроенной функции.Это скомпилируется конечно, но что мы получим?Это зависит от выбора компоновщика: D

0 голосов
/ 29 ноября 2018

В заголовочном файле (назовем его foo.h) вы можете иметь

inline int foo() { /* do stuff */ }

Теперь, если вы включите foo.h в пару файлов cpp, тогда в каждом из них будет определено foo, что было бы ошибкой множественного определения.Поскольку foo помечен как inline, это нормально, потому что все определения одинаковы.

Насколько я понимаю, объявление может быть сделано несколько раз, но определение толькотребуется один раз

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

...