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

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

Восстановить пароль Регистрация
Другие темы раздела
C++ Область видимости анонимного объекта при вызове функции http://www.cyberforum.ru/cpp-beginners/thread1231879.html
Доброго дня. Хотел поинтересоваться: когда уничтожится анонимный объект, переданный как параметр функции? #include <stdio.h> #include <string> using namespace std; class Obj{ private: string s;
C++ STL Заполнить массив размером 10 случайными числами от 0 до 10, отсортировать его по убыванию и вывести Заполнить массив размером 10 случайными числами от 0 до 10, отсортировать его по убыванию и вывести в выходной поток. Добавлено через 20 минут пример, но он с ошибками #include <vector> #include <algorithm> #include <iostream> #include <functional> using namespace std; http://www.cyberforum.ru/cpp-beginners/thread1231849.html
C++ STL Заполнить строку латинским алфавитом и вывести ее в выходной поток
Заполнить строку латинским алфавитом и вывести ее в выходной поток. С++ Добавлено через 20 минут вот пример выполнения, но в нем ошибки #include <string> #include <iostream> using namespace std; int main() { string s;
STL Заполнить двухмерный вектор таблицей умножения и вывести его в выходной поток C++
Заполнить двухмерный вектор таблицей умножения и вывести его в выходной поток. Добавлено через 19 минут пример, но в нем ошибки #include <vector> #include <iostream> #include <iomanip> using namespace std; int main() {
C++ STL Заполнить вектор длинной 10 случайными числами от 0 до 9 и вывести его в выходной поток http://www.cyberforum.ru/cpp-beginners/thread1231846.html
Заполнить вектор длинной 10 случайными числами от 0 до 9 и вывести его в выходной поток.
C++ Используя STL контейнер set заполнить массив Задание 2. Данную задачу решить не используя метод sort, а используя STL контейнер: set. Заполнить массив размером 10 случайными числами от 0 до 10, отсортировать его по возрастанию и вывести в выходной поток. Добавлено через 16 минут пример решения #include <vector> #include <algorithm> #include <iostream> using namespace std; подробнее

Показать сообщение отдельно
DrOffset
6423 / 3797 / 878
Регистрация: 30.01.2014
Сообщений: 6,586
25.07.2014, 19:48     При вынесении определения всегда ли нужно указывать функцию как inline явно?
Цитата Сообщение от 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 передавать один параметр, а не два. С чем конкретно связано такое поведение сказать сложно - это надо смотреть исходники. Однако один уже этот факт должен полностью развеять у тебя веру в непогрешимость и ум современных С++ компиляторов. По крайней мере на какое-то время.
 
Текущее время: 15:57. Часовой пояс GMT +3.
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin® Version 3.8.9
Copyright ©2000 - 2016, vBulletin Solutions, Inc.
Рейтинг@Mail.ru