Форум программистов, компьютерный форум, киберфорум
С++ для начинающих
Войти
Регистрация
Восстановить пароль
 
 
Рейтинг 4.64/11: Рейтинг темы: голосов - 11, средняя оценка - 4.64
-1 / 25 / 4
Регистрация: 27.11.2017
Сообщений: 375
1

Inline функция или нет

29.11.2017, 22:52. Просмотров 2076. Ответов 25
Метки нет (Все метки)


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

Вопрос такой, можно ли в принципе узнать удовлетворил ли он наше ходатайство о присвоении той или иной функции звания inline, или же он это ходатайство отклонил?

И второй вопрос (если, конечно, ответ на первый будет положительным): Как это сделать в принципе?
0
Programming
Эксперт
94731 / 64177 / 26122
Регистрация: 12.04.2006
Сообщений: 116,782
29.11.2017, 22:52
Ответы с готовыми решениями:

Inline функции - на сколько должна быть маленькая функция, чтоб она подошла под inline?
Здравствуйте. Знаю теорию, но не понимаю, на сколько должна быть маленькая функция, чтоб она...

Функция inline
Здравствуйте, много сайтов перечитал, везде теория... Дайте пример плиз, где показывается пример...

inline функция
Помогите плиз, трудности с функцией inline. Внешняя функция exam заносит в объект класса Stud...

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

25
С чаем беда...
Эксперт CЭксперт С++
8493 / 4216 / 1168
Регистрация: 18.10.2014
Сообщений: 9,130
29.11.2017, 23:16 2
Цитата Сообщение от Просто Саша Посмотреть сообщение
Вопрос такой, можно ли в принципе узнать удовлетворил ли он наше ходатайство о присвоении той или иной функции звания inline, или же он это ходатайство отклонил?
Эффект ключевого слова inline двояк. Это, во-первых, эффект для самой функции, и, во-вторых, эффект для вызовов этой функции.

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

Что касается эффекта для самой функции - он к встраиванию кода никакого отношения не имеет вообще. Эффект inline сводится только к тому, что для данной функции изменяется Правило Одного Определения (One Definition Rule): inline-функцию (в отличие от обычных функций) разрешается определять в программе несколько раз, в разных единицах трансляции. Здесь нет никакого "ходатайства" - это эффект всегда проявляется гарантированно и безусловно.

То есть "звание inline" такая функция получает всегда и безусловно. Но это не гарантирует, что все вызовы этой функции будут встраиваться. Может быть вообще ни один не встроится.
1
Заклинатель змей
603 / 502 / 212
Регистрация: 30.04.2016
Сообщений: 2,410
29.11.2017, 23:20 3
Просто Саша, присоеденюсь к мнениюTheCalligrapher. Если верить Шилдту, то встраивания нет для функций с if/else, for, goto
0
С чаем беда...
Эксперт CЭксперт С++
8493 / 4216 / 1168
Регистрация: 18.10.2014
Сообщений: 9,130
29.11.2017, 23:31 4
Цитата Сообщение от DobroAlex Посмотреть сообщение
Если верить Шилдту, то встраивания нет для функций с if/else, for, goto
А вот это как раз таки типичная шилдтовская белиберда. Алгоритмы принятия решения о встраивании, разумеется, у каждого компилятора свои. Одна существенная преграда для встраивания очевидна - это рекурсия. Однако и рекурсию можно встраивать до некоей фиксированной глубины.

Проблемы с встраиванием также могут быть у функций, содержащих вызовы alloca, setjmp/longjmp. Помнится были еще какие-то менее очевидные проблемы с обработкой исключений.

Но "if/else, for, goto" - это из разряда "это было давно и неправда"...

Добавлено через 4 минуты
Многие компиляторы, кстати, предоставляют средства для отслеживания того, встроились ли вызовы inline функций. Для GCC это предупреждение выключается через -Winline. Для MSVC, насколько я помню, аналогичные предупреждения генерируются для __forceinline функций.
1
-1 / 25 / 4
Регистрация: 27.11.2017
Сообщений: 375
29.11.2017, 23:39  [ТС] 5
Цитата Сообщение от TheCalligrapher Посмотреть сообщение
Эффект ключевого слова inline двояк. Это, во-первых, эффект для самой функции, и, во-вторых, эффект для вызовов этой функции.
Может быть он и двояк, я тут спорить не буду, но тока укажу на тот эффект, с которым Вы все согласитесь.
Коли сделали мы функцию inline функцией, то все, во-первых мы распрощались с ее прототипами, и обязаны размещать определение такой функции выше по тексту в том файле, куда мы хотим ее пристроить.

А что там делает компилятор - это интересно, я и задал то поэтому вопрос, можно или нет оценить как сработал компилятор в том или в другом случае.

Добавлено через 6 минут
Кстати, хочу обратить внимание не на вот эти else и goto, а вот на такой факт: В учебниках напрочь отсутствует такая тема inline функции и перегрузка.

Вот что Вы можете сказать по этому поводу?
Вот согласитесь, что вот так с ходу не определишь, разрешается ли перегружать inline функцию, а если разрешается, то как встраивается то или другое. Ведь на стадии компиляции не очень то определишь, что будет вызвано на стадии выполнения.
0
С чаем беда...
Эксперт CЭксперт С++
8493 / 4216 / 1168
Регистрация: 18.10.2014
Сообщений: 9,130
29.11.2017, 23:48 6
Цитата Сообщение от Просто Саша Посмотреть сообщение
Коли сделали мы функцию inline функцией, то все, во-первых мы распрощались с ее прототипами, и обязаны размещать определение такой функции выше по тексту в том файле, куда мы хотим ее пристроить.
С формальной токи зрения это не совсем верно. Спецификация языка лишь говорит, что inline-функция должна быть определена во всех единицах трансляции, в которых она используется, и везде определена одинаково. Требования определять ее именно "выше по тексту" нет. Более того, ремарка в тексте стандарта специально подчеркивает это: "[ Note: A call to the inline function or a use of the inline variable may be encountered before its definition appears in the translation unit. —end note ]". При желании, вы можете сверху воспользоваться прототипом (с inline), а определение поместить вниз.

Цитата Сообщение от Просто Саша Посмотреть сообщение
Вот согласитесь, что вот так с ходу не определишь, разрешается ли перегружать inline функцию, а если разрешается, то как встраивается то или другое. Ведь на стадии компиляции не очень то определишь, что будет вызвано на стадии выполнения.
Не понятно, о чем речь. Никаких особенностей, связанных с inline, перегрузка (overloading) не имеет вообще. Не ясно, откуда взялась "стадия выполнения". Перегрузка функций всегда полностью разрешается (через процесс overload resolution) на стадии компиляции. Всегда на стадии компиляции ясно, какая функция будет вызываться.
1
-1 / 25 / 4
Регистрация: 27.11.2017
Сообщений: 375
29.11.2017, 23:52  [ТС] 7
Кстати вот Стивен Прата в своем учебнике утверждает, что для того чтобы функция стала inline нужно выполнить хотя бы одно из двух:

1) Предварить ОПРЕДЕЛЕНИЕ этой функции словом inline
2) Предварить ОБЪЯВЛЕНИЕ этой функции словом inline (я так понимаю это использовать прототип с inline)

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

Попробовал и вот что вышло:

1) вариант

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
auto main(int argc, char * argv[]) -> int
{
    extern inline auto display() -> void;
    display();
 
    system("pause");
    return EXIT_SUCCESS;
}
 
 
auto inline display() -> void
{
    cout << "Hello World!" << '\n';
}
В первом варианте возникли ошибки сборки.

2) вариант (inline уже только в прототипе)

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
auto main(int argc, char * argv[]) -> int
{
    extern inline auto display() -> void;
    display();
 
    system("pause");
    return EXIT_SUCCESS;
}
 
 
auto display() -> void
{
    cout << "Hello World!" << '\n';
}
Во втором варианте все работает, но непонятно был ли этот inline или же нет.
0
С чаем беда...
Эксперт CЭксперт С++
8493 / 4216 / 1168
Регистрация: 18.10.2014
Сообщений: 9,130
30.11.2017, 00:00 8
Цитата Сообщение от Просто Саша Посмотреть сообщение
Возникает вопрос зачем использовать inline с прототипом, если определение должно стоять выше по тексту.
Не должно.

Цитата Сообщение от Просто Саша Посмотреть сообщение
Попробовал и вот что вышло:
Оба варианта некорректны

7.1.6 The inline specifier
5 The inline specifier shall not appear on a block scope declaration.
А вот так можно

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
#include <iostream>
 
inline auto display() -> void;
 
auto main(int argc, char * argv[]) -> int
{
  display();
}
 
auto display() -> void
{
  std::cout << "Hello World!" << std::endl;
}
1
-1 / 25 / 4
Регистрация: 27.11.2017
Сообщений: 375
30.11.2017, 00:22  [ТС] 9
Ну почему он не должен появляться на уровне блока, если во-первых тоже работает, а во-вторых, зачем он на внешнем, если он нужен только в функции main?
Куда тогда деть принцип минимальных привилегий?

И опять же самый главный вопрос. Ну работает оно и в моем и в Вашем варианте и проверил будет работать даже если функцию display разместить в отдельном файле.
Но то что имеет место inline не факт. Может оно сработало, как вызов обычной функции, хотя по всем канонам, вот такая функция просто обязана быть встраиваемой.
0
С чаем беда...
Эксперт CЭксперт С++
8493 / 4216 / 1168
Регистрация: 18.10.2014
Сообщений: 9,130
30.11.2017, 00:38 10
Цитата Сообщение от Просто Саша Посмотреть сообщение
Ну почему он не должен появляться на уровне блока, если во-первых тоже работает,
"Тоже работает" - это ничего не значит. Если ваш компилятор при компиляции хоть как-то ругнулся на локальное объявление с inline, то он своею задачу выполнил - вас предупредил. А дальше - ваши проблемы. С точки зрения С++ поведение программы не определено.

А если ваш компилятор вообще никак не ругнулся, то это значит, что либо вы его неправильно сконфигурировали (т.е. он не работает в режиме стандартного С++ вообще), либо в компиляторе сидит баг.

GCC исправно ругается на такое: http://coliru.stacked-crooked.... bf0c1dad6b

C++
1
2
3
4
auto main() -> int
{
  inline auto display() -> void;
}
Код
g++ -std=c++17 -pedantic-errors main.cpp
main.cpp: In function 'int main()':
main.cpp:3:28: error: 'inline' specifier invalid for function 'display' declared out of global scope [-Wpedantic]
   inline auto display() -> void;
                            ^~~~
Цитата Сообщение от Просто Саша Посмотреть сообщение
а во-вторых, зачем он на внешнем, если он нужен только в функции main?
Куда тогда деть принцип минимальных привилегий?
Мы пока что говорили о функциях с внешним связыванием. Такая функция в С++ - сущность, живущая на уровне namespace. Какой смысл объявлять ее локально, если определение все равно придется поместить снаружи?

Цитата Сообщение от Просто Саша Посмотреть сообщение
и проверил будет работать даже если функцию display разместить в отдельном файле.
Это опять не имеет никакого отношения к С++. Если функция объявлена inline, то она должна быть определена в этой же единице трансляции. А то, что "будет работать если разместить в отдельном файле" - это уже глюки реализации, к С++ никакого отношения не имеющие.

Цитата Сообщение от Просто Саша Посмотреть сообщение
Но то что имеет место inline не факт. Может оно сработало, как вызов обычной функции, хотя по всем канонам, вот такая функция просто обязана быть встраиваемой.
Это по каким это "канонам" такой вызов обязан быть встраиваемым? В С++ нет никаких канонов, которые бы могли гарантировать встроенность вызова.
1
-1 / 25 / 4
Регистрация: 27.11.2017
Сообщений: 375
30.11.2017, 01:02  [ТС] 11
Никак он не ругается и никаких предупреждений не дает (MS Visual Studio 2017)

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

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

Вот пока, что понял, так это то, что inline дает возможность размещать такие функции в заголовках, а все остальное на уровне любит - не любит, или, как говорится - ОТ ЛУКАВОГО.

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

Добавлено через 4 минуты
Да и еще вот нарыл, что #pragma inline-recursion позволяет инлайнить и рекурсивные функции до 16 уровней.
Ещё какая-то прагма позволяет понижать число этих уровней.
И не будут инлайнится те функции, обращение к которым происходит через указатель.
0
С чаем беда...
Эксперт CЭксперт С++
8493 / 4216 / 1168
Регистрация: 18.10.2014
Сообщений: 9,130
30.11.2017, 01:52 12
Цитата Сообщение от Просто Саша Посмотреть сообщение
Никак он не ругается и никаких предупреждений не дает (MS Visual Studio 2017)
Баг компилятора.

Цитата Сообщение от Просто Саша Посмотреть сообщение
Что значит снаружи? В C++ все функции определяются снаружи, но это ведь не значит, что доступ к ним нужно давать везде, где только можно.
К функциям с внешним связыванием доступ в любом случае есть везде, где только можно. Механизмы "ограничения доступа" сводятся к private и protected на уровне класса и static/unnamed namespace на уровне единицы трансляции, но это не наша тема.

Цитата Сообщение от Просто Саша Посмотреть сообщение
Так мы докатимся и до того, что все переменные глобальными сделаем.
Не вижу аналогии. С переменными у нас есть выбор - они бывают локальными. Локальных функций в С++ нет.

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

Запустите GCC с -Winline - и он сам будет объяснять вам, почему он что-то не заинлайнил.
2
-1 / 25 / 4
Регистрация: 27.11.2017
Сообщений: 375
30.11.2017, 02:04  [ТС] 13
Нет, тут до связывания дело не доходит. Оно здесь вообще ни сбоку припеку и правильно Вы говорите - это не наша тема.

Локальных функций, конечно нет, зато есть локальные области видимости.

Вот последнее более интересно, значит все таки, кто то реализовал такую штуку.
Тогда разумеется вопрос сужается. Вы может быть знаете тогда какой ключ в MS Visual Studio отвечает за подобную функциональность?
0
С чаем беда...
Эксперт CЭксперт С++
8493 / 4216 / 1168
Регистрация: 18.10.2014
Сообщений: 9,130
30.11.2017, 04:08 14
Цитата Сообщение от Просто Саша Посмотреть сообщение
Вы может быть знаете тогда какой ключ в MS Visual Studio отвечает за подобную функциональность?
В MS Visual Studio для этого следует использовать __forceinline вместо inline, а также включить предупреждение C4714 (уровень 4). То есть либо поднять уровень предупреждений в настройках проекта до 4, либо наоборот понизить собственный уровень C4714 до 1

C++
1
#pragma warning(1 : 4714)
Тогда компилятор будет выдавать предупреждение C4714 на вызовы __forceinline функций, которые он по какой-то причине не сумел встроить. Объяснений "почему" он однако не предоставляет - надо разбираться самому.

В описании C4714 перечислены возможные причины.
1
Велосипедист...
349 / 216 / 73
Регистрация: 15.12.2015
Сообщений: 785
30.11.2017, 04:18 15
Пока мы еще совсем не съехали с темы, хочу воспользоваться моментом и уточнить 2 вещи:

1. Правда ли, что ключевое слово inline можно опустить в определении inline-функции, если выше есть объявление ( прототип ) этой функции?
C++
1
2
3
4
5
6
// example
inline void foo();
 
...
 
void foo() {}
2. Можно ли присвоить указателю на функцию адрес функции, помеченной спецификатором inline? У меня сейчас двоякое мнение:
__1) с одной стороны, это UB, ведь если компилятор действительно встроит такую функцию, тогда ее существовать не будет в принципе.
__2) с другой стороны, как мне кажется, если компилятор встретит выражение, где адрес функции ( которую пометили как inline ) попытаются присвоить указателю на функцию, то такая функция встроенной не станет ( спецификатор inline будет проигнорирован ).
C++
1
2
3
4
5
6
// example
inline void foo() {}
 
...
 
void ( *ptr_foo )() = foo;  // is it correct?
Добавлено через 47 секунд
TheCalligrapher
0
2706 / 1875 / 554
Регистрация: 05.06.2014
Сообщений: 5,447
30.11.2017, 04:35 16
Цитата Сообщение от Captain Maxee Посмотреть сообщение
__1) с одной стороны, это UB, ведь если компилятор действительно встроит такую функцию, тогда ее существовать не будет в принципе.
С чего бы? Встраиваем копию функции в каждую точку ее вызова, еще одну копию функции заводим чтоб было на что указывать указателю, и всего делов.
1
Велосипедист...
349 / 216 / 73
Регистрация: 15.12.2015
Сообщений: 785
30.11.2017, 04:39 17
Renji, Это так есть на самом деле, или это Ваше предположение?)
0
2706 / 1875 / 554
Регистрация: 05.06.2014
Сообщений: 5,447
30.11.2017, 04:40 18
Цитата Сообщение от Captain Maxee Посмотреть сообщение
Renji, Это так есть на самом деле, или это Ваше предположение?)
Предположение. В нутро компилятора не заглядывал, но такой вариант видится мне наиболее логичным.
1
С чаем беда...
Эксперт CЭксперт С++
8493 / 4216 / 1168
Регистрация: 18.10.2014
Сообщений: 9,130
30.11.2017, 04:41 19
Цитата Сообщение от Captain Maxee Посмотреть сообщение
1. Правда ли, что ключевое слово inline можно опустить в определении inline-функции, если выше есть объявление ( прототип ) этой функции?
C++
1
2
3
4
5
6
// example
inline void foo();
 
...
 
void foo() {}
Правда. По крайней мере именно так я воспринимаю намерение авторов стандартя языка.

Цитата Сообщение от Captain Maxee Посмотреть сообщение
2. Можно ли присвоить указателю на функцию адрес функции, помеченной спецификатором inline? У меня сейчас двоякое мнение:
__1) с одной стороны, это UB, ведь если компилятор действительно встроит такую функцию, тогда ее существовать не будет в принципе.
__2) с другой стороны, как мне кажется, если компилятор встретит выражение, где адрес функции ( которую пометили как inline ) попытаются присвоить указателю на функцию, то такая функция встроенной не станет ( спецификатор inline будет проигнорирован ).
Можно.

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

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

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

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

Таким образом никакого UB тут нет. И никакого игнорирования inline тут тоже нет. Что с указателями, что без, все работает по одной и той же схеме. В каждой точке кода решение принимается индивидуально: если компилятор захотел/сумел выполнит встраивание, он его выполняет, а в остальных случаях идет работа с обычным телом функции.

В том числе, если в месте вызова функции через указатель "особо умный" компилятор точно знает, на какую функцию этот указатель указывает, то он может сделать прямой вызов и выполнить встраивание. Эта логика распространяется и на объявление виртуальных функций как inline.
1
Велосипедист...
349 / 216 / 73
Регистрация: 15.12.2015
Сообщений: 785
30.11.2017, 05:06 20
Цитата Сообщение от TheCalligrapher Посмотреть сообщение
Вы продолжаете путать две вещи, о которых я говорил ранее: влияние inline на саму функцию и влияние inline на вызовы этой функции в коде.
Да, это так. С первого раза до меня не дошло...)

Цитата Сообщение от TheCalligrapher Посмотреть сообщение
Что касается вызовов (и других ссылок) на функцию из основного кода - то это совсем другая история. Одни вызовы могут быть встроены, другие - не встроены. Если какие-то вызовы отказались не встроены, то для обслуживания таких вызовов компилятором будет сгенерировано обычное тело той же самой функции, единственное на всю программу.
То есть, если все вызовы функции оказались встроенными и надобности в генерировании тела функции не возникло, то оно сгенерировано не будет? ( Если да, можно не отвечать. )

Спасибо.


Не по теме:

Цитата Сообщение от TheCalligrapher Посмотреть сообщение
стандартя
:)

0
IT_Exp
Эксперт
87844 / 49110 / 22898
Регистрация: 17.06.2006
Сообщений: 92,604
30.11.2017, 05:06

Заказываю контрольные, курсовые, дипломные и любые другие студенческие работы здесь или здесь.

Лямбда функция (или нет) для сортировки
Привет В общем, нужно сделать лабу в институте, ничего как всегда не объясняют, просто нужно...

Inline функция в Debug режиме
Добрый вечер! Есть класс, который ссылается на inline функции другого класса. Все реализации...

virtual inline функция-член каласса
Возможно ли сочтание модификаторов virtual и inline для функции-члена каласса? Почему? И может ли...

inline функции vs инструкции inline функций
Здравствуйте. Чтобы не писать повторно код, нужно использовать функции. Но если функции компактные,...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
20
Ответ Создать тему
Опции темы

КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin® Version 3.8.9
Copyright ©2000 - 2021, vBulletin Solutions, Inc.