Если мы определяем функцию-член внутри самого определения класса, обязательно ли она обрабатывается встроенной или это просто запрос к компилятору, который он может игнорировать.
Встроенные функции в C++
Ответы (7)
Да, функции, определенные внутри тела класса, неявно являются inline
.
(Как и в случае с другими функциями, объявленными inline
, это не означает, что компилятор должен выполнять встроенное расширение в местах, где вызывается функция, это просто позволяет разрешенные послабления «правила одного определения» в сочетании с требованием, чтобы определение включаться во все единицы перевода, в которых используется функция.)
Как утверждают другие, метод, определенный в классе, автоматически запрашивается встроенным. Полезно понять почему.
Предположим, что это не так. Вам нужно будет сгенерировать код для такой функции, и везде, где она вызывается, инструкция перехода к подпрограмме должна будет ссылаться на местоположение через компоновщик.
class A {
public:
void f() { ... your code ... }
};
Каждый раз, когда этот код виден, если он не является встроенным, компилятор может только предположить, что он должен быть сгенерирован, поэтому он сгенерирует символ. Предположим, это было так:
A__f_v:
Если бы этот символ был глобальным, то, если бы вы включили этот код класса несколько раз в разные модули, у вас возникла бы ошибка множественного определения символа во время компоновки. Так что это не может быть глобальным. Вместо этого это локальный файл.
Представьте, что вы включаете указанный выше заголовочный файл в несколько модулей. В каждом из них будет создана локальная копия этого кода. Это лучше, чем вообще не компилировать, но вы получаете несколько копий кода, когда вам действительно нужна только одна.
Из этого следует следующий вывод: если ваш компилятор не собирается встраивать функцию, вам значительно лучше объявить ее где-то один раз, а не запрашивать ее встраивание.
К сожалению, то, что является и не является встроенным, не является переносимым. Он определяется автором компилятора. Хорошее эмпирическое правило состоит в том, чтобы всегда делать каждый лайнер, особенно все функции, которые сами просто вызывают функцию, встроенными, поскольку вы удаляете накладные расходы. Все, что меньше трех строк линейного кода, почти наверняка в порядке. Но если у вас есть цикл в коде, вопрос заключается в том, позволит ли компилятор сделать его встроенным, и, что более важно, сколько пользы вы увидите, даже если он сделает то, что вы хотите.
рассмотрите этот встроенный код:
inline int add(int a, int b) { return a + b; }
Он не только почти такой же маленький, как прототип был бы в исходном коде, но и язык ассемблера, сгенерированный встроенным кодом, меньше, чем вызов подпрограммы. Таким образом, этот код меньше и быстрее.
И, если вам случится передавать константы:
int c= add(5,4);
Это разрешается во время компиляции, и кода нет.
В gcc я недавно заметил, что даже если я не встраиваю код, если он локален в файле, они все равно незаметно встроят его. Только если я объявляю функцию в отдельном исходном модуле, они не оптимизируют вызов.
На другом конце спектра предположим, что вы запрашиваете встроенный фрагмент кода из 1000 строк. Даже если ваш компилятор достаточно глуп, чтобы согласиться с этим, единственное, что вы сохраните, — это сам вызов, и цена будет заключаться в том, что каждый раз, когда вы его вызываете, компилятор должен вставлять весь этот код. Если вы вызываете этот код n раз , ваш код увеличивается на размер подпрограммы * n. Таким образом, все, что больше 10 строк, практически не стоит встраивать, за исключением особого случая, когда он вызывается очень небольшое количество раз. Примером этого может быть частный метод, вызываемый только двумя другими.
Если вы запрашиваете встроенный метод, содержащий цикл, это имеет смысл только в том случае, если он часто выполняется небольшое количество раз. Но рассмотрим цикл, который повторяется миллион раз. Даже если код встроен, процент времени, потраченного на вызов, ничтожно мал. Поэтому, если у вас есть методы с циклами, которые в любом случае имеют тенденцию быть больше, их стоит удалить из файла заголовка, потому что они а) будут отклонены компилятором как встроенные и б) даже если они были встроены, как правило не принесет никакой пользы
Это обязательно обрабатывается компилятором как запрос на встроенный код, который он может игнорировать. Есть некоторые идиомы для определения некоторых функций в заголовке (например, пустые виртуальные деструкторы) и некоторые необходимые определения заголовка (функции шаблона), но кроме этого см. GotW #33 для получения дополнительной информации.
Некоторые отмечают, что компилятор может даже встраивать функции, о которых вы его никогда не просили, но я не уверен, что это противоречит цели запроса на встраивание функции.
Он действительно встроен, но любой встроенный запрос может быть проигнорирован компилятором.
Это запрос к компилятору, который он может игнорировать.
inline
.
- person CB Bailey; 23.03.2010
inline
, который компилятор может игнорировать.
- person shoosh; 23.03.2010
Стандарт ISO C++ 2003 года гласит:
7.1.2/2 Объявление функции (8.3.5, 9.3, 11.4) со встроенным спецификатором объявляет встроенную функцию. Спецификатор inline указывает реализации, что встроенная подстановка тела функции в точке вызова предпочтительнее обычного механизма вызова функции
. Реализация не требуется для выполнения этой встроенной
подстановки в точке вызова; однако, даже если эта inline
замена опущена, другие правила для встроенных функций, определенные в 7.1.2, все равно должны соблюдаться.7.1.2/3 Функция, определенная в определении класса, является встроенной
функцией. Спецификатор inline не должен появляться в объявлении функции области блока.7.1.2/4 Встроенная функция должна быть определена в каждой единице перевода
, в которой она используется, и должна иметь точно такое же определение в каждом случае
(3.2). [Примечание: вызов встроенной функции может быть обнаружен
до того, как ее определение появится в единице перевода. ] Если функция с внешней связью объявлена встроенной в одной единице перевода, она должна быть объявлена встроенной во всех единицах перевода, в которых она появляется; диагностика не требуется. Встроенная функция с внешней связью должна иметь один и тот же адрес во всех единицах перевода. Статическая локальная переменная во внешней функции inline
всегда ссылается на один и тот же объект. Строковый литерал во встроенной
внешней функции представляет собой один и тот же объект в разных единицах перевода
.
Есть две вещи, которые не следует смешивать вместе:
- Как вы помечаете функцию как встроенную: определяете ее как встроенную перед сигнатурой или определяете ее в точке объявления;
- Что компилятор будет обрабатывать такую встроенную маркировку: независимо от того, как вы пометили функцию как встроенную, она будет рассматриваться компилятором как запрос.