Означает ли это, что в мире модулей, чтобы компилятор мог оптимизировать вызовы функций, мы должны аннотировать их как inline
, даже если они определены в классе?
В некоторой степени.
Встраивание - это оптимизация "как будто", и встраивание может происходить даже между единицами перевода, если компилятор достаточно умен.
С другой стороны, встраивание проще всего при работе в пределах одной единицы перевода. Таким образом, чтобы обеспечить простое встраивание, объявленная inline
функция должна иметь свое определение в любой единице перевода, где она используется. Это не означает, что компилятор, безусловно, встроит его (или, конечно, не встроит какую-либо неквалифицированную inline
функцию), но это значительно облегчает процесс вставки, так как вставка происходит внутри TU, а не внутри TU. между ними.
Определения членов класса, определенные внутри класса в мире, предшествующем модулю, объявляются inline
неявно. Почему? Потому что определение в классе. В пре-модульном мире определения классов, которые являются общими для TU, разделяются текстовым включением. Поэтому элементы, определенные в классе, будут определены в заголовке, совместно используемом этими TU. Поэтому, если несколько TU используют один и тот же класс, эти несколько TU делают это, включая определение класса и определение его членов, объявленных в заголовке.
То есть, вы все равно включаете определение , так почему бы не сделать это inline
?
Конечно, это означает, что определение функции теперь является частью текста класса. Если вы измените определение элемента, объявленного в заголовке, это приведет к рекурсивной перекомпиляции каждого файла, который включает этот заголовок. Даже если сам интерфейс класса не меняется, вам все равно нужно выполнить перекомпиляцию. Таким образом, создание таких функций неявным образом inline
не меняет этого, так что вы можете также сделать это.
Чтобы избежать этого в мире, предшествующем модулю, вы можете просто определить член в файле C ++, который не будет включен в другие файлы. Вы теряете простое встраивание, но получаете время компиляции.
Но вот в чем дело: это артефакт использования текстового включения в качестве средства доставки класса в несколько мест.
В модульный мир, вы, вероятно, захотите определить каждую функцию-член внутри самого класса, как мы видим в других языках, таких как Java, C#, Python и тому подобное. Это сохраняет разумность локальности кода и предотвращает необходимость повторного ввода одной и той же сигнатуры функции, тем самым удовлетворяя потребности DRY.
Но если все члены определены в определении класса, то по старым правилам все эти члены будут inline
. И для того, чтобы модуль разрешил функции быть inline
, артефакт двоичного модуля должен включать определение этих функций. Это означает, что каждый раз, когда вы изменяете хотя бы одну строку кода в таком определении функции, модуль должен быть собран вместе с каждым модулем, зависящим от него, рекурсивно.
Удаление неявного - inline
в модулях дает пользователям те же полномочия, которые они имели в дни включения текста, без необходимости переносить определение из класса. Вы можете выбрать, какие определения функций являются частью модуля, а какие нет.