Подставляемые функции в C++

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

18
задан shreyasva 23 March 2010 в 16:36
поделиться

7 ответов

Да, функции, определенные внутри тела класса, неявно встроены в .

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

19
ответ дан 30 November 2019 в 07:33
поделиться

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

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

3
ответ дан 30 November 2019 в 07:33
поделиться

Это запрос к компилятору, который он может игнорировать.

2
ответ дан 30 November 2019 в 07:33
поделиться

Есть две вещи, которые не следует объединять в одну кучу:

  1. Как вы помечаете функцию как встроенную: определите ее встроенной перед подписью или определите ее в точка объявления;
  2. Что компилятор будет обрабатывать такую ​​встроенную маркировку: независимо от того, как вы пометили функцию как встроенную, она будет рассматриваться компилятором как запрос.
1
ответ дан 30 November 2019 в 07:33
поделиться

Стандарт ISO C ++ 2003 говорит:

7.1.2 / 2 Объявление функции (8.3.5, 9.3, 11.4) со встроенным спецификатором объявляет встроенную функцию . Встроенный спецификатор указывает реализации, что встроенная подстановка тела функции в точке вызова должна быть предпочтительнее обычного вызова функции
механизм. Реализация не требуется для выполнения этой встроенной замены
в точке вызова; однако , даже если эта встроенная замена
является опущен, остальные правила для встроенных функций, определенные в 7.1.2, по-прежнему должны соблюдаться.

7.1.2 / 3 Функция, определенная в определении класса, является встроенной функцией
. Встроенный спецификатор не должен появляться в объявлении функции области видимости блока.

7.1.2 / 4 Встроенная функция должна быть определена в каждой единице перевода в
, в которой она используется, и должна иметь точно такое же определение во всех
{{ 1}} случай (3.2). [Примечание: вызов встроенной функции может встречаться
до того, как ее определение появится в блоке перевода . ] Если функция с внешней связью объявлена ​​ встроенной в одной единице перевода, она должна быть объявлена ​​встроенной во всех единицах перевода, в которых она { Появляется {1}}; Диагностика не требуется. Встроенная функция с внешней связью должна иметь один и тот же адрес во всех единицах перевода. Статическая локальная переменная в функции extern inline
всегда ссылается на один и тот же объект . Строковый литерал во встроенной функции
extern - это один и тот же объект в разных единицах перевода
.

2
ответ дан 30 November 2019 в 07:33
поделиться

Она действительно встроена, но любой встроенный запрос может игнорироваться компилятором.

3
ответ дан 30 November 2019 в 07:33
поделиться

Как утверждали другие, метод, определенный в классе, автоматически запрашивается встроенным . Полезно понять, почему.

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

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 строк, практически не стоит встраивать, за исключением особого случая, когда оно вызывается очень небольшое количество раз. Примером этого может быть частный метод, вызываемый только двумя другими.

Если вы запрашиваете встраивание метода, содержащего цикл, это имеет смысл только в том случае, если он часто выполняется небольшое количество раз. Но рассмотрим цикл, который повторяется миллион раз.Даже если код встроен, процент времени, затрачиваемого на вызов, крошечный. Поэтому, если у вас есть методы с циклами в нем, которые в любом случае имеют тенденцию быть больше, их стоит удалить из файла заголовка, потому что они: а) будут отклоняться компилятором как встроенные и б) даже если они были встроены, обычно не принесет никакой выгоды

5
ответ дан 30 November 2019 в 07:33
поделиться
Другие вопросы по тегам:

Похожие вопросы: