C ++ встроенные функции и объявления

Прежде всего, извините за мой английский.

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

Например:

inline void print();

void print();

или же:

void print();
inline print();

Что означает переопределение функции с различными встроенными спецификаторами? Более сложный пример:

#include <iostream>

void print();

int main()
{
print(); // (1)
}

inline void print() { std::cout << "HELLO" << std::endl; }

С исключительной точки зрения C ++, а не с точки зрения компилятора, print функционировать встроенный по очереди (1)?

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

0

Решение

Это предложение:

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

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

Конечно, компилятор может игнорировать inline ключевое слово для решения, является ли функция встроенной или нет — и она МОЖНО использовать встроенные функции, НЕ помеченные как встроенные. Однако компилятор использует inline ключевое слово [и его эквивалент «body, объявленного внутри структуры»] при генерации окончательного результата, чтобы избежать нескольких определений функции, код которой генерировался несколько раз в разных единицах компиляции. Например:

 foo.h:
inline int foo() { return 42; }

a.cpp:
#include "foo.h"
...

b.cpp:
#include "foo.h"
...

Если функция foo не был объявлен inlineкомпоновщик будет жаловаться на множественные определения функции foo если мы свяжем результаты a.cpp и b.cpp в один исполняемый файл.

Тем не менее, вы правы в том, что компилятор не решит встроить функции на основе inline ключевое слово, но основано на других аспектах, таких как, сколько раз вызывается функция, является ли источник функции «видимым» для компилятора и т. д.

Как простое правило:

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

Поскольку встраивание происходит ближе к концу компиляции (когда ВСЕ исходный код был проанализирован и помещен в некую разновидность AST (абстрактного синтаксического дерева) или аналогичной формы), обычно ли функция используется до или после точки использования Разумеется, если вызов относится к функции, которая не является частью AST (исходный код недоступен), у компилятора нет другого выбора, кроме как НЕ встроить его.

Исключение «невозможно встроить, когда источник недоступен» — это так называемая LTO «Оптимизация времени соединения». Традиционный компоновщик просто возьмет набор инструкций машинного кода и вставит их вместе в любом порядке, в котором они перечислены, без какого-либо знания о том, что функция [упрощенное представление, достаточное для этого обсуждения], а затем исправит любые адреса для функций и переменных. что компилятор не может разрешить напрямую. В отличие от этого, LTO сохраняет «объектные файлы» в качестве промежуточного представления, и окончательная генерация машинного кода выполняется на этапе компоновщика. Это означает, что у компоновщика достаточно информации, например, для перемещения кода, чтобы встроить код одной функции в другую. Эта технология доступна в последних выпусках, например, gcc (4.9.0 имеет LTO в качестве полной функции, более ранние версии имели немного меньшую поддержку)

1

Другие решения

inline позволяет определять внешнюю функцию связи (по существу идентично) в двух или более единицах перевода.

что означает определение его в заголовке.

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


inline просто накапливается. функция не может статьinline,


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


1 C ++ 17 10.1.6 / 5 (dcl.inline / 5): «Встроенная функция или переменная должна быть определена в каждой единице перевода, в которой она используется odr, и должна иметь абсолютно одинаковое определение в каждом случае»

1

По вопросам рекламы [email protected]