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

Прокомментировать код функций, генерирующих другие функции (лямбды) - C++

Восстановить пароль Регистрация
 
kquick
6 / 6 / 5
Регистрация: 15.05.2014
Сообщений: 101
05.01.2016, 21:49     Прокомментировать код функций, генерирующих другие функции (лямбды) #1
В функциональном программировании функции могут возвращать другие функции.

Корректно ли делать это в C++ так, как показано ниже? Какие могут быть проблемы?
Пример 1:
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
#include <iostream>
#include <functional>
 
std::function<int(int)> gen_fun(int n) {
    return [&n](int x) { return x*n; };
}
 
int main() {
    std::function<int(int)> f = gen_fun(5);
    std::cout << f(1) << std::endl;
    std::cout << f(2) << std::endl;
    std::cout << f(3) << std::endl;
}
Пример 2:
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <iostream>
#include <functional>
 
std::function<int()> gen_clo() {
    static int n = 1;
    return [=]() { return n++; };
}
 
int main() {
    std::function<int()> f1 = gen_clo();
    std::function<int()> f2 = gen_clo();
    std::cout << f1() << std::endl;
    std::cout << f1() << std::endl;
    std::cout << f2() << std::endl;
}
Во втором примере в замыканиях разделяемая общая n. Как генерировать независимые n при каждом создании такого замыкания?
Similar
Эксперт
41792 / 34177 / 6122
Регистрация: 12.04.2006
Сообщений: 57,940
05.01.2016, 21:49     Прокомментировать код функций, генерирующих другие функции (лямбды)
Посмотрите здесь:

Прокомментировать код C++
Прокомментировать код C++
Не применяя библиотечных функций, напишите код функции C++
Прокомментировать код C++
C++ Прокомментировать код
После регистрации реклама в сообщениях будет скрыта и будут доступны все возможности форума.
Melg
416 / 152 / 62
Регистрация: 23.09.2013
Сообщений: 306
05.01.2016, 22:01     Прокомментировать код функций, генерирующих другие функции (лямбды) #2
Сообщение было отмечено автором темы, экспертом или модератором как ответ
kquick, С Новым Годом Вас!
Проблемы с первым вариантом связаны с тем, что Вы захватываете в лямде по ссылке значение переменной n, область видимости которой ограничена самой функцией gen_fun. Таким образом после того как Вы возвращаете функцию из gen_fun - один из захваченных по ссылке объектов перестает существовать. Это приводит к тому, что память, выделенная под данный объект может быть использована под другие объекты. Данная проблема эквивалентна попытке возврата ссылки на объект локальный для функции.
Данная ситуация приводит к неопределенному поведению. Т.е. программа в этом случае может делать всё, что угодно. Например, вести себя "якобы" правильно, и делать то что Вы ожидаете:
http://ideone.com/Ga0sbZ
А может и нет:
https://coderpad.io/XW22R4RH

на случай, если кто-то удалит код программы, там получается вывод :
9:47pm - Melg running 13 lines of C++
5
0
0
Согласитесь, один и тот же текст программы, дающий разное поведение из-за компиляции двумя разными компиляторами, поддерживающими один и тот же стандарт (или с разными флагами) - не лучшая ситуация.

Пример 2 не так проблематичен. В данном случае вы просто вводите статическую переменную, значение которой будет инициализировано при первом вызове. Проблема данного кода состоит в том, что полученная из gen_clo функция будет иметь скрытую зависимость от глобального состояния. Таким образом тривиальная проверка:
std::function<int()> f = gen_clo();
assert(f(), f()); будет проваливаться.
Кроме того данный код не является потоко-безопасным.
Насколько мне известно, в программировании имеется стремление к написанию кода, в котором любые зависимости между частями программы выражены в явной форме. Это позволяет проще думать о таком коде, проще его отлаживать и тестировать.

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

Добавлено через 1 минуту
Зы. Я отвечал на первую постановку вопроса:
Корректно ли делать это в C++ так, как показано ниже? Какие могут быть проблемы?
anti-k
 Аватар для anti-k
226 / 74 / 23
Регистрация: 17.07.2015
Сообщений: 774
Завершенные тесты: 1
05.01.2016, 22:04     Прокомментировать код функций, генерирующих другие функции (лямбды) #3
Цитата Сообщение от kquick Посмотреть сообщение
std::function<int(int)> gen_fun(int&Melg, n) {
* * return [&n](int x) { return x*n; };
}
Вот так будет ок?
Melg
416 / 152 / 62
Регистрация: 23.09.2013
Сообщений: 306
05.01.2016, 22:06     Прокомментировать код функций, генерирующих другие функции (лямбды) #4
Сообщение было отмечено автором темы, экспертом или модератором как ответ
anti-k, Пример иллюстрирующий возможную реализацию корректного захвата:
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
#include <iostream>
#include <functional>
 
std::function<int(int)> gen_fun(int n) {
    return [&n](int x) { return x*n; };
}
 
std::function<int(int)> right_gen_fun(const int &n) {
    return [&n](int x) { return x*n; };
}
 
void TestDefault() {
    std::function<int(int)> f = gen_fun(5);
    std::cout << f(1) << std::endl;
    std::cout << f(2) << std::endl;
    std::cout << f(3) << std::endl;
}
 
void TestRight() {
    std::function<int(int)> f = right_gen_fun(5);
    int value = 0;
    std::cout << f(++value) << std::endl;
    std::cout << f(++value) << std::endl;
    std::cout << f(++value) << std::endl;
}
 
int main() {
  TestDefault();
  TestRight();
}
Пруф работоспособности:
http://ideone.com/XlApi6
Yandex
Объявления
05.01.2016, 22:06     Прокомментировать код функций, генерирующих другие функции (лямбды)
Ответ Создать тему
Опции темы

Текущее время: 16:25. Часовой пояс GMT +3.
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin® Version 3.8.9
Copyright ©2000 - 2016, vBulletin Solutions, Inc.
Рейтинг@Mail.ru