Форум программистов, компьютерный форум, киберфорум
С++ для начинающих
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск Заказать работу  
 
Рейтинг 4.67/6: Рейтинг темы: голосов - 6, средняя оценка - 4.67
-41 / 49 / 5
Регистрация: 10.01.2017
Сообщений: 1,915

Накладные расходы std::function, std::bind, анонимные функции

06.07.2023, 15:20. Показов 1360. Ответов 11
Метки нет (Все метки)

Студворк — интернет-сервис помощи студентам
Здравствуйте,

Позвольте два вопроса:


1)Подскажите пожалуйста, вот на таком просто примере:

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
int my_func(double db_, std::string& string_)
{
    std::cout << db_     << std::endl;
    std::cout << string_ << std::endl;
 
    return 0;
}
 
 
int main()
{
        std::string my_string = "Hello";
 
 
    std::function<void()> f_;
 
    f_= std::bind(my_func, 4.5, my_string);
    f_= [&my_string] { my_func(4.5, my_string); };
 
}
Правильно ли я понимаю, что std::bind и лямбда функции - не работают "бесплатно" и в итоге по сравнению с прямым вызовом функции - будет менее эффективным ?

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



2)Если мне нужно чтобы условный пользователь вызвал мою функцию в конкретном участке кода, то мне тогда придется поместить std::function в какой то контейнер и этот контейнер поместить в нужный участок кода откуда и вызывать "указатели на функции или методы классов".


Но вот проблемка: Предположим мне с сервера приходят данные и я их условно помещаю/добавляю в std::string.

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

Но вот такая ситуация: s


C++
1
2
3
4
5
6
7
8
9
10
td::string string_, std::list<std::function<void()>vec_func;
 
-Сервер прислал данные-1: string_ + data_from_server;
vec_func.push_back(std::bind(my_func, 4.5, my_string););
 
-Сервер прислал данные-2: string_ + data_from_server;
vec_func.push_back(std::bind(my_func, 4.5, my_string););
 
-Сервер прислал данные-3: string_ + data_from_server;
vec_func.push_back(std::bind(my_func, 4.5, my_string););

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

Однако передавать строку в функцию по значению глупо и вот как быть не знаю.
0
Programming
Эксперт
39485 / 9562 / 3019
Регистрация: 12.04.2006
Сообщений: 41,671
Блог
06.07.2023, 15:20
Ответы с готовыми решениями:

Std::bind, std::mem_fun, std::mem_fn
В чем разница между функциями std::bind, std::mem_fun, std::mem_fn?

Как можно еще использовать std::placeholders вне в связки с std::bind?
Добрый день! Как можно еще использовать std::placeholders вне в связки с std::bind?

В чем отличия между std::cref() и std::bind()?
В документации не понял, что делает bind() ? И чем отличается cref() от операции взятия адреса? int x; int *y = &amp;x;...

11
631 / 526 / 104
Регистрация: 05.08.2022
Сообщений: 2,810
06.07.2023, 16:01
Как минимум пункт 2 легко проверить:

код
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
#include <iostream>
#include <list>
#include <functional>
 
int my_func(double db_, std::string& string_)
{
    std::cout << db_     << std::endl;
    std::cout << string_ << std::endl;
 
    return 0;
}
 
 
int main() {
 
    std::list<std::function<void()>> vec_func;
 
    std::string my_string;
 
    my_string = "-A-";
    vec_func.push_back(std::bind(my_func, 1, my_string));
    my_string = "-B-";
    vec_func.push_back(std::bind(my_func, 2, my_string));
    my_string = "-C-";
    vec_func.push_back(std::bind(my_func, 3, my_string));
    
    for (auto& f : vec_func)
        f();
}


Выводит:
1
-A-
2
-B-
3
-C-

т.е. не смотря на ссылки корректно запоминается "окружение" в момент добавления лямбды в контейнер.
Как оно это делает - для меня полная загадка.

Причем функции реально выполняются на самом деле "потом", в цикле for, а не сразу.
Вот пример про это.

Добавил глобальную переменную.

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
#include <iostream>
#include <list>
#include <functional>
 
char * g_ch = "";
 
int my_func(double db_, std::string& string_)
{
    std::cout << db_     << std::endl;
    std::cout << g_ch    << std::endl;
    std::cout << string_ << std::endl;
 
    return 0;
}
 
 
int main() {
 
    std::list<std::function<void()>> vec_func;
 
    std::string my_string;
    
    g_ch = "AAA";
 
    my_string = "-A-";
    vec_func.push_back(std::bind(my_func, 1, my_string));
    my_string = "-B-";
    vec_func.push_back(std::bind(my_func, 2, my_string));
    my_string = "-C-";
    vec_func.push_back(std::bind(my_func, 3, my_string));
    
    g_ch = "ZZZ";
 
    for (auto& f : vec_func)
        f();
}
Выводит:
1
ZZZ
-A-
2
ZZZ
-B-
3
ZZZ
-C-

т.е. выводится значение глоб. переменной на момент for, на момент реального вызова функции
1
-41 / 49 / 5
Регистрация: 10.01.2017
Сообщений: 1,915
06.07.2023, 16:14  [ТС]
Цитата Сообщение от KSergey9 Посмотреть сообщение
т.е. не смотря на ссылки корректно запоминается "окружение" в момент добавления лямбды в контейнер.
Как оно это делает - для меня полная загадка.

Я не правильно привел вызов std::bind:

Аналог анонимно функции, которая захватывает my_string по ссылке будет:

C++
1
f_= std::bind(my_func, 4.5, std::ref(my_string));
И такого результата уже не получится.

То есть std::bind создавала временную копию my_string, теперь же хранит только ссылку на my_string.
0
19491 / 10097 / 2460
Регистрация: 30.01.2014
Сообщений: 17,805
06.07.2023, 16:30
Optimus11,
1) да
2) если действительно нужна копия, то не глупо (возможно копия не нужна, а нужен move)
2
Комп_Оратор)
Эксперт по математике/физике
 Аватар для IGPIGP
9005 / 4706 / 630
Регистрация: 04.12.2011
Сообщений: 14,003
Записей в блоге: 16
06.07.2023, 16:34
Лямбда и bind это способы создать функтор и его объект. Если в конструктор передаётся константная ссылка, то это не значит что поле объекта будет инициализированно как ссылка, кроме случая, когда оно само является константной ссылкой. Иначе оно инициализирутся копированием.
А если вы передаёте std::reference, то поле будет создано и инициализировано как std::reference.
Накладные расходы от сохранения вызова в объекте функтора в другом. Они заключаются в создании объекта функтора. Я когда-то экспериментировал на эту тему немного. В блоге:
https://www.cyberforum.ru/blog... g4325.html
1
Вездепух
Эксперт CЭксперт С++
 Аватар для TheCalligrapher
12923 / 6790 / 1818
Регистрация: 18.10.2014
Сообщений: 17,180
06.07.2023, 21:47
Цитата Сообщение от Optimus11 Посмотреть сообщение
Правильно ли я понимаю, что std::bind и лямбда функции - не работают "бесплатно" и в итоге по сравнению с прямым вызовом функции - будет менее эффективным ?
Нет, никаких накладных расходов у лямбды нет. Любые потенциальные расходы являются полностью анализируемыми на стадии компиляции и, соответственно, исключаемыми качественным компилятором.

std::function<> - совсем другое дело. std::function<> привносит накладные расходы времени выполнения, которые в общем случае исключить невозможно.

Поэтому вопрос в том, о чем именно ваш вопрос. Вы как будто не совсем разделяете лямбды и std::function<>, хотя на самом деле это совершенно разные вещи.

Цитата Сообщение от Optimus11 Посмотреть сообщение
Если так, то можно ли предположить, что в тех случаях, когда удобно применить такое связывание - попытка реализовать это напрямую обычными функциями все равно в итоге привела бы к таким же "накладным расходам" ?
Это как это предлагается реализовывать функциональность std::bind "напрямую обычными функциями"?
1
-41 / 49 / 5
Регистрация: 10.01.2017
Сообщений: 1,915
06.07.2023, 21:57  [ТС]
Цитата Сообщение от TheCalligrapher Посмотреть сообщение
Это как это предлагается реализовывать функциональность std::bind "напрямую обычными функциями"?
Ну типа такого:


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
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
class Class2
{
 
 
public:
    void my_method_1(int i)
    {
        std::cout << "class_2_method_1: " << i << std::endl;
    }
 
    void my_method_2(int i, const std::string& s)
    {
        int_private = int_private + i;
        std::cout << "class_2_method_2: " << i << ": " << s << std::endl;
    }
 
    void my_method_3(std::string s)
    {
        std::cout << "my_method_3:" << s << std::endl;
    }
 
 
private:
 
    int int_private = 1000;
 
};
 
 
class kvazi_bind 
{
public:
 
    kvazi_bind(Class2& c2, std::string& string_) : c2_(c2), string_(string_) {}
 
 
    void operator()() 
    {
        c2_.my_method_3(string_);
    }
 
private:
 
    Class2& c2_;
    std::string string_;
};
 
 
 
int main()
{
 
    Class2 c2;
        std::string string_ = "Hello";
 
 
    std::function<void()> f_kvazi_bind;
    kvazi_bind kvazi_bind_(c2, string_);
    f_kvazi_bind = kvazi_bind_;
 
 
    std::function<void()> f_bind;
    f_bind = std::bind(&Class2::my_method_3, &c2, string_);
 
 
    f_kvazi_bind();
    f_bind();
 
}
0
19491 / 10097 / 2460
Регистрация: 30.01.2014
Сообщений: 17,805
08.07.2023, 15:30
Optimus11, так и чем кончилось дело?
0
-41 / 49 / 5
Регистрация: 10.01.2017
Сообщений: 1,915
08.07.2023, 17:23  [ТС]
Цитата Сообщение от DrOffset Посмотреть сообщение
Optimus11, так и чем кончилось дело?
Я даже не знаю как и ответить

Я просто решил, что использование std::function вместе с лямбдой слишком удобно, чтобы их не использовать, особенно, если вызовов таким образом функций будет не много.
0
Комп_Оратор)
Эксперт по математике/физике
 Аватар для IGPIGP
9005 / 4706 / 630
Регистрация: 04.12.2011
Сообщений: 14,003
Записей в блоге: 16
08.07.2023, 18:20
Цитата Сообщение от Optimus11 Посмотреть сообщение
Я просто решил, что использование std::function вместе с лямбдой слишком удобно, чтобы их не использовать, особенно, если вызовов таким образом функций будет не много.
Это нормально. Но поверили ли вы, в то что создание объектов на стеке не тратит время в рантайме? Код на создание объектов компилируется во время компиляции, но его выполнение происходит во время выполнения. Стек отыгрывает рабочие области, в функциях срабатывают те или иные ветви ветвления по свитчам и ифам и создаются объекты только тех ветвей, что будут выполнены в данном, конкретном, вызове. Легко увидеть, что это потребует различных затрат времени. Из этого следует, что функтор дороже функции, как правило. А лямбды это синтаксические выражения для автогенерации функторов. Чем шире их список захвата (особенно по значению) тем дороже должен быть вызов.
1
Вездепух
Эксперт CЭксперт С++
 Аватар для TheCalligrapher
12923 / 6790 / 1818
Регистрация: 18.10.2014
Сообщений: 17,180
08.07.2023, 18:50
Цитата Сообщение от Optimus11 Посмотреть сообщение
Ну типа такого:
Но это уже не "обычные функции". Это классы с перегруженным operator (). Это просто "ручная" реализация лямбды, т.е. closure-объекта. Никакой разницы в эффективности с обычной лямбдой здесь не будет, если не вмешаются какие-то глюки-недоделки-странности конкретного компилятора.

Еще раз:

1. Накладные расходы лямбды/closure-объекта равны нулю. (Разумеется, в рамках понимания того, что расходы на захват того, что вам нужно захватить, накладными не являются.) Более того, функциональность лямбды является полностью анализируемой на этапе компиляции, в т.ч. полностью встраиваемой (inline), т.е. анализируемой после погружения в контекст окружающего кода.

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

3. std::function<> - это отдельная надстройка над всеми вызываемыми (callable) сущностями, которая привносит свои накладные расходы. Сведение всех вызываемых сущностей к "общему знаменателю" как правило требует применения полиморфизма времени выполнения, то влечет за собой неустраняемые накладные расходы. При очень локальном использовании std::function<> умный компилятор возможно сможет исключить эти расходы, но в общем случае устранить их не получится.
2
Комп_Оратор)
Эксперт по математике/физике
 Аватар для IGPIGP
9005 / 4706 / 630
Регистрация: 04.12.2011
Сообщений: 14,003
Записей в блоге: 16
09.07.2023, 21:00
Цитата Сообщение от Optimus11 Посмотреть сообщение
Правильно ли я понимаю, что std::bind и лямбда функции - не работают "бесплатно" и в итоге по сравнению с прямым вызовом функции - будет менее эффективным ?
Я думаю, я верно понял вопрос. Функции, std::bind и лямбда функции, это вызываемые объекты. В пределах этой категории их вполне правомерно сравнивать. И да. Как уже было сказано, функторы дороже. Создание объектов происходит во время выполнения, в общем случае. Что касается встраивания, то любой вызов может быть встроенным, а может и не быть. Вот вызов лямбды, который нельзя встроить, на тех же основаниях, что было бы нельзя встроить и код обычной функции. Но вариантов "невстаивания" гораздо больше, а это просто для примера, в качестве противодействия, безаппеляционным выбросам :
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
#include <iostream>
 
struct Base
{
    virtual void vfn()const{std::cout<<"Base";}
    virtual ~Base()=default;
};
 
struct Derived : public Base
{
    void vfn()const override {std::cout<<"Derived";}
};
 
int main()
{
    int cond(0);
    std::cout<<"Base : 0 Derived : 1\n";
    std::cin>>cond;
    Base* pb= cond? new Derived : new Base;
    auto anonymous_functor=[pb](){pb->vfn();} ;
 
    anonymous_functor();//этот вызов нельзя встроить как ты его не анонизируй в компил-тайме
 
    return 0;
}
0
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
inter-admin
Эксперт
29715 / 6470 / 2152
Регистрация: 06.03.2009
Сообщений: 28,500
Блог
09.07.2023, 21:00
Помогаю со студенческими работами здесь

Какая реализация лучше? std::pointer_to_binary_function vs std::function
Какая реализация (set_p или set_f) лучше /современнее / эффективнее ? pointer_to_binary_function в С++11 объявлен как deprecated. Правильно...

Использование std::function в std::thread
Нужно вызвать function fnc в новом потоке. Как сделать? function &lt;void(vector&lt;char&gt;)&gt; fnc; void test(vector&lt;char&gt; data) { ...

[Error] no matching function for call to 'std::basic_ifstream<char>::basic_ifstream(std::string&)
Пишу на DEV-C++, есть функция, в ней не работает 4 строчка: ifstream reading(FileName); Выдает ошибку: In function 'void...

Std::function and std::vector
Как положить обёртки в вектор? Не используя библиотеку boost. function&lt;void(int)&gt; first_func() = one(); function&lt;void(int)&gt;...

Не воспринимает ни std::cout, ни std::cin. Вобщем ничего из std. Также не понимает iostream
Здравствуйте! Я хотел начать изучать язык C++. Набрал литературы. Установил Microsoft Visual C++ 2005 Express Edition. Образ диска...


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

Или воспользуйтесь поиском по форуму:
12
Ответ Создать тему
Новые блоги и статьи
PhpStorm 2025.3: WSL Terminal всегда стартует в ~
and_y87 14.12.2025
PhpStorm 2025. 3: WSL Terminal всегда стартует в ~ (home), игнорируя директорию проекта Симптом: После обновления до PhpStorm 2025. 3 встроенный терминал WSL открывается в домашней директории. . .
Access
VikBal 11.12.2025
Помогите пожалуйста !! Как объединить 2 одинаковые БД Access с разными данными.
Новый ноутбук
volvo 07.12.2025
Всем привет. По скидке в "черную пятницу" взял себе новый ноутбук Lenovo ThinkBook 16 G7 на Амазоне: Ryzen 5 7533HS 64 Gb DDR5 1Tb NVMe 16" Full HD Display Win11 Pro
Музыка, написанная Искусственным Интеллектом
volvo 04.12.2025
Всем привет. Некоторое время назад меня заинтересовало, что уже умеет ИИ в плане написания музыки для песен, и, собственно, исполнения этих самых песен. Стихов у нас много, уже вышли 4 книги, еще 3. . .
От async/await к виртуальным потокам в Python
IndentationError 23.11.2025
Армин Ронахер поставил под сомнение async/ await. Создатель Flask заявляет: цветные функции - провал, виртуальные потоки - решение. Не threading-динозавры, а новое поколение лёгких потоков. Откат?. . .
Поиск "дружественных имён" СОМ портов
Argus19 22.11.2025
Поиск "дружественных имён" СОМ портов На странице: https:/ / norseev. ru/ 2018/ 01/ 04/ comportlist_windows/ нашёл схожую тему. Там приведён код на С++, который показывает только имена СОМ портов, типа,. . .
Сколько Государство потратило денег на меня, обеспечивая инсулином.
Programma_Boinc 20.11.2025
Сколько Государство потратило денег на меня, обеспечивая инсулином. Вот решила сделать интересный приблизительный подсчет, сколько государство потратило на меня денег на покупку инсулинов. . . .
Ломающие изменения в C#.NStar Alpha
Etyuhibosecyu 20.11.2025
Уже можно не только тестировать, но и пользоваться C#. NStar - писать оконные приложения, содержащие надписи, кнопки, текстовые поля и даже изображения, например, моя игра "Три в ряд" написана на этом. . .
Мысли в слух
kumehtar 18.11.2025
Кстати, совсем недавно имел разговор на тему медитаций с людьми. И обнаружил, что они вообще не понимают что такое медитация и зачем она нужна. Самые базовые вещи. Для них это - когда просто люди. . .
Создание Single Page Application на фреймах
krapotkin 16.11.2025
Статья исключительно для начинающих. Подходы оригинальностью не блещут. В век Веб все очень привыкли к дизайну Single-Page-Application . Быстренько разберем подход "на фреймах". Мы делаем одну. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2025, CyberForum.ru