Форум программистов, компьютерный форум CyberForum.ru
Наши страницы

С++ для начинающих

Войти
Регистрация
Восстановить пароль
 
 
Рейтинг: Рейтинг темы: голосов - 10, средняя оценка - 4.70
TierX
20 / 20 / 0
Регистрация: 28.02.2014
Сообщений: 138
#1

При вынесении определения всегда ли нужно указывать функцию как inline явно? - C++

22.07.2014, 11:21. Просмотров 1350. Ответов 46
Метки нет (Все метки)

Бьярни пишет
Если в описании класса функция-член определена, а не только описана, то она считается подстановкой. Это значит, например, что при
трансляции функций, использующихchar_stack из предыдущего примера, не будет использоваться
никаких операций вызова функций, кроме реализации операций вывода!
Кликните здесь для просмотра всего текста

C++
1
2
3
4
5
6
7
8
9
10
class char_stack { 
int size; 
char* top; 
char* s; 
public: 
  char_stack(int sz) { top=s=new char[size=sz]; } 
  ~char_stack() { delete[] s; } // деструктор
  void push(char c) { *top++ = c; } 
  void pop() { return *--top; } 
};

Далее
Функцию-член можно описать со спецификацией inline и вне описания класса:
Кликните здесь для просмотра всего текста
C++
1
2
3
4
5
6
7
8
9
10
11
12
class char_stack { 
int size; 
char* top; 
char* s; 
public: 
char pop(); 
// ... 
}; 
inline char char_stack::pop() 
{ 
return *--top; 
}

Собсно при вынесении определения нужно всегда указывать функцию как inline явно? Или она всеже будет ею изначально как описано в начале. Слово можно както неоднозначно.
0
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
Similar
Эксперт
41792 / 34177 / 6122
Регистрация: 12.04.2006
Сообщений: 57,940
22.07.2014, 11:21
Здравствуйте! Я подобрал для вас темы с ответами на вопрос При вынесении определения всегда ли нужно указывать функцию как inline явно? (C++):

Зачем нужно явно указывать тип переменной? - C++
Всем привет! Объясните зачем нужно явно указывать тип переменной? Например в С++ это обязательно, а php нет. Просто говорят что не...

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

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

Как адаптировать функцию под inline - C++
Дана функция: bool Str(ifstream &f, const char *s) { char buff; return !(f.get(buff,...

Как избавиться от необходимости явно указывать тип в enum? - C#
Как сделать, чтобы для перечислений не нужно было явно указывать тип int? switch (direction) { case...

что нужно указывать в образце поиска при создании макроса - MS Access
Подскажите, пожалуйста, что нужно указывать в образце поиска при создании макроса, если я хочу с помощью кнопки отыскать нужную фамилию?

46
DrOffset
7351 / 4451 / 1009
Регистрация: 30.01.2014
Сообщений: 7,292
22.07.2014, 18:23 #31
Цитата Сообщение от TierX Посмотреть сообщение
Следовательно явно inline в срр файле обьявлять нельзя.
Можно. Зависит от предполагаемого использования.
Вот например, есть класс А:
C++
1
2
3
4
5
6
7
8
class A
{
public:
    void bar(); 
 
private:
    void foo();
};
Метод bar часть интерфейса класса, предполагается для вызова пользователями класса. Метод foo вспомогательный и вызывается внутри bar. Мы можем написать так:
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
#include .....
 
inline void A::foo()
{
// some work
}
 
void A::bar()
{
// some work
    foo();
// some work
}
Даем рекомендацию компилятору, что метод foo может быть встроен, но происходить это будет только в рамках данного cpp. Это в принципе может дать определенный профит в некоторых ситуациях.
Цитата Сообщение от TierX Посмотреть сообщение
Чото много както мутной жижи с этим всем.
Ну большинство (большинство) даже самых диких, на первый взгляд, особенностей языка на самом деле довольно логичны. Особенно, если знать историю.
0
Psilon
Master of Orion
Эксперт .NET
5902 / 4799 / 634
Регистрация: 10.07.2011
Сообщений: 14,407
Записей в блоге: 5
Завершенные тесты: 4
22.07.2014, 19:36 #32
DrOffset, какие эвфемизмы для "древние костыли" вы однако придумываете
0
DrOffset
7351 / 4451 / 1009
Регистрация: 30.01.2014
Сообщений: 7,292
22.07.2014, 19:59 #33
Цитата Сообщение от Psilon Посмотреть сообщение
какие эвфемизмы для "древние костыли" вы однако придумываете
Да, нет, это вполне реальная проблема, только на пальцах. Ну не хватало компилятору мозгов самому функцию встраивать в той ситуации.
0
Psilon
Master of Orion
Эксперт .NET
5902 / 4799 / 634
Регистрация: 10.07.2011
Сообщений: 14,407
Записей в блоге: 5
Завершенные тесты: 4
22.07.2014, 20:54 #34
DrOffset, ну в случае какого-нибудь #pragma loop, а вот что не хватает мозгов заинлайнить - это конечно для С++ компилятора странно. Я понимаю, шарповый JIT, у него тупо времени не хватает все варианты просмотреть, а нот С++ - он намного умнее

Ладно, не буду холиварить. Есть - и есть
0
DrOffset
7351 / 4451 / 1009
Регистрация: 30.01.2014
Сообщений: 7,292
22.07.2014, 23:55 #35
Цитата Сообщение от Psilon Посмотреть сообщение
Я понимаю, шарповый JIT, у него тупо времени не хватает все варианты просмотреть, а нот С++ - он намного умнее
Это сейчас так. А вот 6-10 лет назад было иначе. Это был далеко не новый компилятор. Если не изменяет склероз, то это gcc 3.3.6, да и реальный пример как бы не совсем такой простой, как я показал, на просто примере может быть и он заинлайнил бы.
И это я еще молчу про приколы 2.95.2.
Хотя в общем-то это не важно. Не знаю чем тебе не понравился мой пример, но лично мне кажется, что он вполне безобидный. Да и холиварить на эту тему нельзя, я просто рассказал опыт.

Не по теме:

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

1
Psilon
Master of Orion
Эксперт .NET
5902 / 4799 / 634
Регистрация: 10.07.2011
Сообщений: 14,407
Записей в блоге: 5
Завершенные тесты: 4
23.07.2014, 09:06 #36
Цитата Сообщение от DrOffset Посмотреть сообщение
Вот у меня была ситуация, что из-за ошибки кодогенератора компилятора расчетная программа целых два года выдавала неверные результаты, а человек который ее писал (на всякий случай, этот человек не я), даже сформулировал целую теорию почему так получилось, чтобы оправдаться перед начальством и заказчиками, представляешь, никто даже не задумался о том, что это может быть за ошибка из-за этого. И так логично все выходило, расчеты-то верные на бумаге, все формулы миллиард раз проверены и сам код прошерстили раз 50 разные люди, а потом бац, оказалось кодогенератор чудит. А я тот человек который в итоге эту ошибку нашел. Вот если бы я тебе рассказал такую историю, ты бы тоже не поверил?
я всему верю, у чего пруфы есть

а с подобной ситуацией я уже в рассказах переноса вычислений на GPGPU сталкивался, где ISO754 постольку постольку выполняется, fast math, тудыть его
0
Croessmah
Эксперт CЭксперт С++
13514 / 7672 / 866
Регистрация: 27.09.2012
Сообщений: 18,887
Записей в блоге: 3
Завершенные тесты: 1
23.07.2014, 12:57 #37
Для ознакомления: реализация класса в .h файле хорошо или плохо?
0
DrOffset
7351 / 4451 / 1009
Регистрация: 30.01.2014
Сообщений: 7,292
25.07.2014, 19:48 #38
Цитата Сообщение от Psilon Посмотреть сообщение
я всему верю, у чего пруфы есть
Ну вот кстати насчет пруфов. Ситуация-то не особо поменялась, стало конечно лучше, но разница определенно есть. Вполне достаточная для того, что бы считать фразу
какие эвфемизмы для "древние костыли" вы однако придумываете
слегка поспешной.
Значит, вот есть такой простейший пример:
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
// test.h
#ifndef TEST_H_INCLUDED
#define TEST_H_INCLUDED
 
class A
{
public:
    void bar();
 
private:
    void foo();
};
 
#endif // TEST_H_INCLUDED
 
// test.cpp
#include "test.h"
#include <cstdio>
 
/*inline*/ void A::foo()
{
    printf("%s", "void foo()");
}
 
void A::bar()
{
    foo();
}
 
//main.cpp
#include "test.h"
 
int main()
{
    A a;
 
    a.bar();
}
Я его собирал компилятором "GCC version 4.7.2 20121109 (Red Hat 4.7.2-8)". Опции: -O2.
Вот дизассемблер без inline:
Кликните здесь для просмотра всего текста

Assembler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
.LC0:
    .string "void foo()"
.LC1:
    .string "%s"
    .text
    .align 2
    .p2align 4,,15
    .globl  _ZN1A3fooEv
    .type   _ZN1A3fooEv, @function
_ZN1A3fooEv: ; A::foo
.LFB12:
    .cfi_startproc
    sub esp, 28
    .cfi_def_cfa_offset 32
    mov DWORD PTR [esp+4], OFFSET FLAT:.LC0
    mov DWORD PTR [esp], OFFSET FLAT:.LC1
    call    printf
    add esp, 28
    .cfi_def_cfa_offset 4
    ret
    .cfi_endproc
.LFE12:
    .size   _ZN1A3fooEv, .-_ZN1A3fooEv
    .align 2
    .p2align 4,,15
    .globl  _ZN1A3barEv
    .type   _ZN1A3barEv, @function
_ZN1A3barEv: ; A::bar
.LFB13:
    .cfi_startproc
    jmp _ZN1A3fooEv
    .cfi_endproc

Как видно, никакого встраивания по-умолчанию не произошло, вместо этого компилятор сгенерировал jmp. В старой версии в моем примере выше был честный call.
Теперь поставим inline:
Кликните здесь для просмотра всего текста
Assembler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
.LC0:
    .string "void foo()"
.LC1:
    .string "%s"
    .text
    .align 2
    .p2align 4,,15
    .globl  _ZN1A3barEv
    .type   _ZN1A3barEv, @function
_ZN1A3barEv: ; A::bar
.LFB13:
    .cfi_startproc
    sub esp, 28
    .cfi_def_cfa_offset 32
    mov DWORD PTR [esp+4], OFFSET FLAT:.LC0
    mov DWORD PTR [esp], OFFSET FLAT:.LC1
    call    printf
    add esp, 28
    .cfi_def_cfa_offset 4
    ret
    .cfi_endproc

Тут inline не только позволил выполнить встраивание, но и инициировал оптимизацию, которая вообще удалила foo из бинарника.
4.7.2 не такой уж старый компилятор, как видно из версии, всего 2 года давности. Однако какая разница в выводе.
Для полноты картины я скомпилировал этот же пример компилятором clang (version 3.3 (tags/RELEASE_33/rc2)).
Версия без inline (AT&T синтаксис, не пугаемся):
Кликните здесь для просмотра всего текста
Assembler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
    .globl  _ZN1A3fooEv
    .align  16, 0x90
    .type   _ZN1A3fooEv,@function
_ZN1A3fooEv:                            # @_ZN1A3fooEv
# BB#0:
    subl    $12, %esp
    movl    $.L.str1, 4(%esp)
    movl    $.L.str, (%esp)
    calll   printf
    addl    $12, %esp
    ret
.Ltmp0:
    .size   _ZN1A3fooEv, .Ltmp0-_ZN1A3fooEv
 
    .globl  _ZN1A3barEv
    .align  16, 0x90
    .type   _ZN1A3barEv,@function
_ZN1A3barEv:                            # @_ZN1A3barEv
# BB#0:
    subl    $12, %esp
    movl    $.L.str1, 4(%esp)
    movl    $.L.str, (%esp)
    calll   printf
    addl    $12, %esp
    ret
.Ltmp1:
    .size   _ZN1A3barEv, .Ltmp1-_ZN1A3barEv
 
    .type   .L.str,@object          # @.str
    .section    .rodata.str1.1,"aMS",@progbits,1
.L.str:
    .asciz   "%s"
    .size   .L.str, 3
 
    .type   .L.str1,@object         # @.str1
.L.str1:
    .asciz   "void foo()"
    .size   .L.str1, 11

Clang действительно умнее, еще бы. Однако, добавление inline все-так же влияет на ситуацию:
Кликните здесь для просмотра всего текста
Assembler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
    .globl  _ZN1A3barEv
    .align  16, 0x90
    .type   _ZN1A3barEv,@function
_ZN1A3barEv:                            # @_ZN1A3barEv
# BB#0:
    subl    $12, %esp
    movl    $.L.str1, 4(%esp)
    movl    $.L.str, (%esp)
    calll   printf
    addl    $12, %esp
    ret
.Ltmp0:
    .size   _ZN1A3barEv, .Ltmp0-_ZN1A3barEv
 
    .type   .L.str,@object          # @.str
    .section    .rodata.str1.1,"aMS",@progbits,1
.L.str:
    .asciz   "%s"
    .size   .L.str, 3
 
    .type   .L.str1,@object         # @.str1
.L.str1:
    .asciz   "void foo()"
    .size   .L.str1, 11

Здесь применена упомянутая выше оптимизация и функция foo удалена из бинарника.
В общем не так тут все однозначно, как многим представляется. А 5-8 лет назад все было гораздо плачевнее.
В защиту GCC: более новые версии, все-таки выполняют встраивание в этом случае.
По секрету скажу, что и 4.7.2 выполняет встраивание автоматически (правда немного странно, вставляя jmp на printf), если в функцию printf передавать один параметр, а не два. С чем конкретно связано такое поведение сказать сложно - это надо смотреть исходники. Однако один уже этот факт должен полностью развеять у тебя веру в непогрешимость и ум современных С++ компиляторов. По крайней мере на какое-то время.
3
Psilon
Master of Orion
Эксперт .NET
5902 / 4799 / 634
Регистрация: 10.07.2011
Сообщений: 14,407
Записей в блоге: 5
Завершенные тесты: 4
25.07.2014, 19:53 #39
DrOffset, огромное спасибо за анализ!
А что с -О3 ?

Добавлено через 1 минуту
Цитата Сообщение от DrOffset Посмотреть сообщение
Однако один уже этот факт должен полностью развеять у тебя веру в непогрешимость и ум современных С++ компиляторов. По крайней мере на какое-то время.
я сравниваю с JITами, у которых все намного плачевнее - они даже tail recursion зачастую не разворачивают, т.к. им отведено определенное время на компиляцию и оптимизацию По сравнению с ними даже -О2 - это небо и земля.
0
DrOffset
7351 / 4451 / 1009
Регистрация: 30.01.2014
Сообщений: 7,292
25.07.2014, 19:59 #40
Цитата Сообщение от Psilon Посмотреть сообщение
А что с -О3 ?
С -О3 вывод эквивалентен выводу clang. Однако у нас -О3 забанен на административном уровне. Вследствие более глобальных и фатальных прецедентов с генерацией кода.

Добавлено через 3 минуты
Цитата Сообщение от Psilon Посмотреть сообщение
я сравниваю с JITами, у которых все намного плачевнее - они даже tail recursion зачастую не разворачивают, т.к. им отведено определенное время на компиляцию и оптимизацию По сравнению с ними даже -О2 - это небо и земля
Это я понимаю. Просто получается, в контексте обсуждения мой пример с inline до сих пор имеет смысл, поэтому костылем и тем более древним его уже нельзя назвать А то, что у компилятора в машинный код больше возможностей - это естественно и я согласен полностью.
0
Psilon
Master of Orion
Эксперт .NET
5902 / 4799 / 634
Регистрация: 10.07.2011
Сообщений: 14,407
Записей в блоге: 5
Завершенные тесты: 4
25.07.2014, 20:07 #41
Цитата Сообщение от DrOffset Посмотреть сообщение
С -О3 вывод эквивалентен выводу clang. Однако у нас -О3 забанен на административном уровне. Вследствие более глобальных и фатальных прецедентов с генерацией кода.
а можно поподробнее? Я просто ничего серьезней лаб по прогарммированию на плюсах не писал, промышленная разработка у меня на шарпах, поэтому не сталкивался с такими проблемами
Цитата Сообщение от DrOffset Посмотреть сообщение
Это я понимаю. Просто получается, в контексте обсуждения мой пример с inline до сих пор имеет смысл, поэтому костылем и тем более древним его уже нельзя назвать А то, что у компилятора в машинный код больше возможностей - это естественно и я согласен полностью.
ну ничего, шарп обещает перебраться на С++ компилятор в ближайшие год-два, с соответствующими последствиями вроде нативности и как следствие снятии необходимости иметь .Net framework на конечной машине. Так что еще поборемся

Добавлено через 2 минуты
DrOffset, кстати насчет JIT, мне нравится его вариация в MS Singularity - компиляция при установке программы. Тут совмещается качество AOH-компиляции с объемом информации JIT-компилятора о целевой машине. То есть компилируется со всеми оптимизациями в машкод, но учитывается процессор (а значит, может эффективно использоваться SIMD и прочие плюшки), объем памяти и т.д. и т.п.
1
DrOffset
7351 / 4451 / 1009
Регистрация: 30.01.2014
Сообщений: 7,292
25.07.2014, 20:19 #42
Цитата Сообщение от Psilon Посмотреть сообщение
а можно поподробнее?
Сложно будет это рассказать не нарушив NDA, т.к. конкретные примеры потребуют показать код.
Если в общем, то при высоких уровнях оптимизации, есть вероятность, что оптимизатор сломает код. Эта вероятность присутствует и на более низких уровнях, но она гораздо реже проявляется. Вот мой предыдущий рассказ про вычислительный модуль как раз из этой серии. Там была всего лишь -O2 и она ломала код. И 2 года все думали что это нормально, потому что автор алгоритма это обосновал допустимыми погрешностями. Все было хорошо, пока на определенных входных данных мы не начали получать просто бешеные, совершенно нереальные числа. Вот тогда-то уже я стал искать причины и выяснилось, что дело в оптимизации, и перевод с -O2 на -О1 решил проблему. А вообще, хорошей практикой для таких задач является индивидуальный набор параметров оптимизации. К чему в конце концов мы и перешли.

Цитата Сообщение от Psilon Посмотреть сообщение
Так что еще поборемся
Не вижу смысла в такой борьбе, у каждого инструмента своя задача. Если для инструмента нет задачи, то его просто не используют
0
Psilon
Master of Orion
Эксперт .NET
5902 / 4799 / 634
Регистрация: 10.07.2011
Сообщений: 14,407
Записей в блоге: 5
Завершенные тесты: 4
25.07.2014, 20:24 #43
Цитата Сообщение от DrOffset Посмотреть сообщение
Не вижу смысла в такой борьбе, у каждого инструмента своя задача. Если для инструмента нет задачи, то его просто не используют
хорошо, когда есть более 1 способа решить задачу Каждый выбирает тот, который ему удобнее.

Цитата Сообщение от DrOffset Посмотреть сообщение
Сложно будет это рассказать не нарушив NDA, т.к. конкретные примеры потребуют показать код.
Если в общем, то при высоких уровнях оптимизации, есть вероятность, что оптимизатор сломает код. Эта вероятность присутствует и на более низких уровнях, но она гораздо реже проявляется. Вот мой предыдущий рассказ про вычислительный модуль как раз из этой серии. Там была всего лишь -O2 и она ломала код. И 2 года все думали что это нормально, потому что автор алгоритма это обосновал допустимыми погрешностями. Все было хорошо, пока на определенных входных данных мы не начали получать просто бешеные, совершенно нереальные числа. Вот тогда-то уже я стал искать причины и выяснилось, что дело в оптимизации, и перевод с -O2 на -О1 решил проблему.
недавно натыкался на статейку на похожую тему, авось будет интересно
0
DrOffset
7351 / 4451 / 1009
Регистрация: 30.01.2014
Сообщений: 7,292
25.07.2014, 20:28 #44
Цитата Сообщение от Psilon Посмотреть сообщение
недавно натыкался на статейку на похожую тему
Не, ну тут UB. Это про другое. У нас UB не было. Более новые компиляторы (проверяли на трех платформах) работали нормально и с -О2 и даже с -О3. Да и ручной анализ кода не выявил никаких ошибок (а статический тем более). Но проблема в сертификации, нам нельзя использовать не сертифицированный компилятор, как следствие выкручиваться приходится на том, что имеем.
1
Psilon
Master of Orion
Эксперт .NET
5902 / 4799 / 634
Регистрация: 10.07.2011
Сообщений: 14,407
Записей в блоге: 5
Завершенные тесты: 4
25.07.2014, 20:32 #45
DrOffset, я же и говорю про "похожую", а не "Такую же" Разница-то в любом случае есть: компилятор оптимизирует UB, не нарушая логики алгоритма (хотя и неочевидно для программиста), у вас же оптимизация как раз-таки нарушала логику. Я бы не поверил, если бы не знал, как та же оптимизация переупорядочиванием инструкций в многопотоке может повлиять (если неграмотно расставить барьеры).
0
25.07.2014, 20:32
MoreAnswers
Эксперт
37091 / 29110 / 5898
Регистрация: 17.06.2006
Сообщений: 43,301
25.07.2014, 20:32
Привет! Вот еще темы с ответами:

Нужно ли всегда использовать using при работе с файлами - C#
using (FileStream fs = new FileStream(path,FileMode.Open,FileAccess.ReadWrite)) { using (StreamReader fr = new StreamReader(fs)) ...

При составлении программ нужно всегда сначала математическую модель составлять? - Pascal
Если сразу пытаться писать и исправлять ошибки по ходу дела, то это плохо?

Как указывать аргументы при вызове одного метода из другого? - C#
Добрый день! Прошу прощения за совсем примитивизм: public void panel1_paint(Object sender, PaintEventArgs e) { ...

Server 2012 Как сделать так, чтобы при подключении по RDP не указывать порт? - Windows Server
Добрый день поменял порт через регистр с 3389 на 3811 , теперь надо при подключении порт указывать а можно что то сделать на сервере...


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

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

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