Что означает то, чтобы функция C++ была подставляемой?

Я помещаю превосходный ответ JLBorges на аналогичный вопрос дословно из cplusplus.com, так как это наиболее краткое объяснение, которое я прочитал по этому вопросу.

] В шаблоне, который мы пишем, есть два типа имен, которые можно использовать - зависимые имена и не зависимые имена. Зависимое имя - это имя, которое зависит от параметра шаблона; неизменяемое имя имеет то же значение, независимо от параметров шаблона.

Например:

template< typename T > void foo( T& x, std::string str, int count )
{
    // these names are looked up during the second phase
    // when foo is instantiated and the type T is known
    x.size(); // dependant name (non-type)
    T::instance_count ; // dependant name (non-type)
    typename T::iterator i ; // dependant name (type)

    // during the first phase, 
    // T::instance_count is treated as a non-type (this is the default)
    // the typename keyword specifies that T::iterator is to be treated as a type.

    // these names are looked up during the first phase
    std::string::size_type s ; // non-dependant name (type)
    std::string::npos ; // non-dependant name (non-type)
    str.empty() ; // non-dependant name (non-type)
    count ; // non-dependant name (non-type)
}

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

На первом этапе анализатор должен знать, является ли зависимое имя именем типа или имени не-типа. По умолчанию зависимым именем считается имя не-типа.

Использовать ключевое слово typename только в объявлениях шаблонов и определениях, приведенных ниже.

blockquote>

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

24
задан jonrsharpe 19 February 2015 в 21:52
поделиться

9 ответов

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

, Это может улучшить скорость (никакой вызов функции), но чрезмерное увеличение размера кода причин (если функция используется 100 раз, у Вас теперь есть 100 копий)

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

Это также позволяет Вам помещать целую функцию в заголовочный файл, вместо того, чтобы реализовывать ее в cpp файле (который Вы наклоняете так или иначе, с тех пор Вы получаете неразрешенное внешнее, если она была объявлена встроенная, если, конечно, только, что cpp файл использовал ее).

37
ответ дан Fire Lancer 28 November 2019 в 22:12
поделиться

Это означает одну вещь и одну вещь только: то, что компилятор будет игнорировать повторные определения функции.

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

IT НЕ ОЗНАЧАЕТ НИЧЕГО БОЛЬШЕ!

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

повторные определения Игнорирования вещь помнить.

31
ответ дан DrPizza 28 November 2019 в 22:12
поделиться

А также другие (совершенно корректные) ответы о последствиях производительности inline, в C++, необходимо также отметить это, позволяют Вам безопасно помещать функцию в заголовок:

// my_thing.h
inline int do_my_thing(int a, int b) { return a + b; }

// use_my_thing.cpp
#include "my_thing.h"
...
    set_do_thing(&do_my_thing);

// use_my_thing_again.cpp
...
    set_other_do_thing(&do_my_thing);

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

Без inline ключевое слово, большинство компиляторов дало бы ошибку о повторном определении, например, для MSVC:

use_my_thing_again.obj : error LNK2005: "int __cdecl do_my_thing(int,int)" (?do_my_thing@@YAHHH@Z) already defined in use_my_thing.obj
<...>\Scratch.exe : fatal error LNK1169: one or more multiply defined symbols found
21
ответ дан Simon Buchan 28 November 2019 в 22:12
поделиться

@OldMan

, который компиляторы только встраивают не отмеченный как подставляемые функции, ТОЛЬКО ЕСЛИ Вы запрашиваете его сделать так.

, Только если "запросом" Вы имеете в виду, "включают оптимизацию".

Его корректное только на effcts nto casuse.

Это корректно в обоих.

Встроенный не генерируют дополнительной информации, которую может использовать компоновщик. Compiel 2 объектных файла и проверка. Это позволяет повторные определения exaclty, потому что символы не экспортируются! Не потому что это - его цель!

, Что Вы имеете в виду, "символы не экспортируются"? подставляемые функции не статичны. Их имена видимы; у них есть внешняя связь. Заключить в кавычки из Стандарта C++:

пустой h (); встройте пустой h ();//внешняя связь

встроенная пустота l (); освободите l ();//внешняя связь

вещью повторных определений является очень цель. Это обязательно:

подставляемая функция должна быть определена в каждой единице перевода, в которой она используется и должна иметь точно то же определение в каждом случае (3.2). [Отметьте: с вызовом к подставляемой функции можно встретиться, прежде чем ее определение появляется в единице перевода.], Если функция с внешней связью объявляется встроенная в одной единице перевода, это должно быть объявлено встроенное во всех единицах перевода, в которых это появляется; никакая диагностика не требуется. Подставляемая функция с внешней связью должна иметь тот же адрес во всех единицах перевода.

3
ответ дан DrPizza 28 November 2019 в 22:12
поделиться

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

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

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

FAQ C++ делает хорошее задание объяснения запутанности ключевого слова: http://www.parashift.com/c++-faq-lite/inline-functions.html#faq-9.3

1
ответ дан Michael Labbé 28 November 2019 в 22:12
поделиться

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

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

можно читать больше в статье MSDN о встроенном - http://msdn.microsoft.com/en-us/library/z8y1yy88.aspx

2
ответ дан Franci Penov 28 November 2019 в 22:12
поделиться

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

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

0
ответ дан Chris Jester-Young 28 November 2019 в 22:12
поделиться

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

0
ответ дан John Ferguson 28 November 2019 в 22:12
поделиться

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

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

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

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

, Почему?

Пример: в заголовочном файле inlinetest.h

int foo();
inline int bar();

В единице компиляции inlinetest.cpp

 int foo(){ int r = bar(); return r; }


 inline int bar(){ return 5;};

Тогда в Компиляции main.cpp

 #include "inlinetest.h"
 int main()
 {
  foo();
 //bar();
  }

один объектный файл за один раз. Если Вы не комментируете, что вызов "панели" у Вас будет ошибка. Поскольку подставляемая функция только реализована на inlinetest.o объектном файле и не экспортируется. В то же время функция нечто, очень вероятно встроил в него код функции панели (так как панель является одной строкой никакая операция ввода-вывода тогда его вероятное, которое будет встроено)

, Но если бы в заголовочном файле Вы объявили подставляемую функцию и реализовали его встроенный тогда, Вы были бы в состоянии использовать его в любой единице компиляции, которая включает тот заголовок. ("пример кода");

Удаляют встроенное ключевое слово, и компилятор НЕ вызовет ошибку даже с панелью, заходят основной И не встраивают, произойдет, если Вы не просите, чтобы компилятор встроил все функции. Это не стандартное поведение на большинстве компиляторов.

0
ответ дан OldMan 28 November 2019 в 22:12
поделиться
Другие вопросы по тегам:

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