C ++ вызывает встроенную функцию из другого файла cpp

Я пытаюсь понять несколько основ о extern, static и т.д. и пробовал следующий пример, но я не понимаю, почему я не могу вызвать функцию только потому, что она (возможно) встроена.

Мой первый файл: F1.cpp

 #include <iostream>
 
 void Modify();
 int i;

 int main() {
      i = 1;

      std::cout << "i = " << i << std::endl;
      Modify();
      std::cout << "i = " << i << std::endl;
 
      return 0;
 }

Второй файл: F2.cpp

#include <iostream>

extern int i;

inline void Modify() {
    i = 99;

    std::cout << "i = " << i << std::endl;

}

С ключевым словом inline в F2.cpp я получаю: undefined ссылку на Modify () в моем файле F1.cpp. Удалив его, код компилируется и работает нормально.

Я предполагаю, что ключевое слово inline в C ++ имеет какое-то поведение, подобное ключевому слову static?

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

Спасибо за вашу помощь !


person Jonathan Taws    schedule 22.12.2014    source источник
comment
@MikeSeymour: Спасибо за исправление! Я только что узнал кое-что новое.   -  person Cornstalks    schedule 22.12.2014
comment
Стандарт гласит: встроенная функция должна быть определена в каждой единице перевода, в которой она используется.   -  person n. 1.8e9-where's-my-share m.    schedule 22.12.2014


Ответы (4)


Согласно стандарту C ++ (спецификаторы функций 7.1.2)

4 .... Если функция с внешней связью объявлена ​​встроенной в одной единице перевода, она должна быть объявлена ​​встроенной во всех единицах перевода, в которых она появляется; Диагностика не требуется.

а также

4 Встроенная функция должна быть определена в каждой единице перевода, в которой она используется odr, и должна иметь точно такое же определение во всех случаях.

В C (раздел 6.7.4 Спецификаторы функций стандарта C) спецификатор функции inline для внешних функций имеет другую семантику.

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

person Vlad from Moscow    schedule 22.12.2014
comment
Я принял ваш ответ, хотя другие ответы верны и хорошо объяснены. Пойду с подсчетом голосов :) - person Jonathan Taws; 23.12.2014

Я предполагаю, что ключевое слово inline в C ++ имеет какое-то поведение, подобное ключевому слову static?

Похожи, но разные. Имя по-прежнему имеет внешнюю связь, и программа ведет себя так, как будто существует только одно определение (например, функция везде имеет один и тот же адрес и только один экземпляр любых статических переменных). Эффекты inline:

  • Функция может быть определена в нескольких единицах перевода, если все определения идентичны. Обычные функции можно определить только один раз.
  • Функция должна быть определена во всех единицах перевода, которые ее используют. Это позволяет компилятору опускать не встроенное определение, если оно ему не нужно.

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

person Mike Seymour    schedule 22.12.2014
comment
Я не совсем понимаю, что касается второго пункта. Это позволяет компилятору опускать нестроечное определение, если оно ему не нужно. Связано ли это с тем, что компилятор должен выбирать, должен ли он быть встроен или нет? - person Jonathan Taws; 22.12.2014
comment
@Hawknight: Да; если он встраивает каждый вызов функции, и вы ничего не делаете (например, не берете его адрес), для чего требуется не-встроенное определение, тогда нет необходимости тратить время и пространство на его создание. - person Mike Seymour; 22.12.2014

Да, inline имеет значение, очень похожее на static. Конкретное требование стандарта (§ [basic.def.odr] / 3):

Встроенная функция должна быть определена в каждой единице перевода, в которой она используется odr.

В этом случае вы определили функцию inline в одной единице перевода, но только объявили ее в другой, поэтому вы не соответствуете вышеуказанному требованию, чтобы она была определен в ЕП, где он используется.

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

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

person Jerry Coffin    schedule 22.12.2014
comment
Спасибо за разъяснения, теперь я понял! Что касается внешней ссылки inline, как вы это делаете? - person Jonathan Taws; 22.12.2014
comment
@Hawknight: имя имеет внешнюю связь по умолчанию, но вы все равно должны определять его в каждой ЕП. По сути, внешняя связь означает, что если компилятор решает не расширять определение встроенным, он всегда будет создавать одно (и только одно) внешнее определение, поэтому любой вызов, который не раскрывается inline будет соответствовать этому определению. - person Jerry Coffin; 22.12.2014

Когда вы объявляете функцию inline, только единица трансляции, в которой она определена (в данном случае F2.cpp), имеет доступ к этой функции. Если вместо этого вы поместите его в файл заголовка (скажем, F.h) и #include "F.h" как в F1.cpp, так и в F2.cpp, тогда функция inline будет определена дважды, по одному разу в каждой единице перевода. Обычно это вызывает ошибку компоновщика, но, поскольку вы объявили функцию inline, компоновщик не знает о вашей функции Modify().

person wolfPack88    schedule 22.12.2014