Форум программистов, компьютерный форум, киберфорум
С++ для начинающих
Войти
Регистрация
Восстановить пароль
 
Рейтинг 4.80/30: Рейтинг темы: голосов - 30, средняя оценка - 4.80
42 / 42 / 17
Регистрация: 25.04.2014
Сообщений: 499
1

Разобраться с std::bind, плейсхолдеры

22.06.2015, 02:24. Просмотров 5630. Ответов 15
Метки нет (Все метки)


Здравствуйте, чтобы разобраться с основами 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
#include <functional>
#include <vector>
 
void print(int i, int j) {
    std::cout<<"--"<<std::endl;
}
 
void printt(int i, int j, int z) {
    std::cout<<i<<" "<<j<<" "<<z<<std::endl;
}
 
class CAmb {
public:
    CAmb() {}
    void printt(int i, int j, int z) {
        std::cout<<i<<" "<<j<<" "<<z<<std::endl;
    }
 
    void print(int i, int j) {
        std::cout<<i<<" "<<j<<std::endl;
    }
};
 
int _tmain(int argc, _TCHAR* argv[])
{
    std::vector<CAmb> vec1(5);
    int i =  3;
    int j = 4;
    int z = 5;
 
    auto pntr1 = std::bind(std::mem_fn(&CAmb::print), std::placeholders::_1, i, j);
    auto pntr2 = std::bind(&CAmb::printt, std::placeholders::_1,  i, j, z);
    auto pntr3 = std::bind(print, i, j);
    auto pntr4 = std::bind(printt, i, j, z);
 
    return 0;
}
сам, я так и не смог разобраться почему std:: placeholders необходимы в pntr1 и pntr2(без них ошибка) и в чем принципиальная разница между, например, pntr2 и pntr4 т.е. как плейсхолдеры "меняют" привязку к функциям-членам класса и просто функциям(если написать
C++
1
auto pntr3 = std::bind(print, std::placeholders::_1, i, j);
то будет ошибка)? прошу, подскажите кто знает
0
Programming
Эксперт
94731 / 64177 / 26122
Регистрация: 12.04.2006
Сообщений: 116,782
22.06.2015, 02:24
Ответы с готовыми решениями:

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() от операции взятия адреса? ...

std::bind
Можно ли, и если да, то как с помощью этой функции сделать следующее: auto var=std::bind(&amp;f,...

15
Эксперт С++
8391 / 3932 / 858
Регистрация: 15.11.2014
Сообщений: 8,869
22.06.2015, 02:38 2
Лучший ответ Сообщение было отмечено tapochka как решение

Решение

Цитата Сообщение от tapochka Посмотреть сообщение
std:: placeholders
обозначает, что на его месте ожидается аргумент,
с которым нужно запустить функтор.

так понятнее:

http://rextester.com/LXNV38035

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
#include <functional>
#include <vector>
#include <iostream>
 
void print(int i, int j) {
    std::cout<<i<<" "<<j<<std::endl;
}
 
void printt(int i, int j, int z) {
    std::cout<<i<<" "<<j<<" "<<z<<std::endl;
}
 
class CAmb {
public:
    CAmb() {}
    void printt(int i, int j, int z) {
        std::cout<<i<<" "<<j<<" "<<z<<std::endl;
    }
 
    void print(int i, int j) {
        std::cout<<i<<" "<<j<<std::endl;
    }
};
 
int main()
{
    std::vector<CAmb> vec1(5);
    int i = 3;
    int j = 4;
    int z = 5;
 
    CAmb obj;
    
    auto pntr1 = std::bind(std::mem_fn(&CAmb::print), std::placeholders::_1, i, j);
    pntr1(obj);
    
    auto pntr2 = std::bind(&CAmb::printt, std::placeholders::_1,  i, j, z);
    pntr2(obj);
    
    auto pntr3 = std::bind(print, i, j);
    pntr3();
    
    auto pntr4 = std::bind(printt, i, j, z);
    pntr4();
}
Добавлено через 1 минуту
здесь доп. материал:
Применение вложенных boost::bind
1
шКодер самоучка
2127 / 1843 / 902
Регистрация: 09.10.2013
Сообщений: 4,029
Записей в блоге: 7
22.06.2015, 02:50 3
Лучший ответ Сообщение было отмечено tapochka как решение

Решение

bind создает новый объект-функцию из указанной функции и параметров
параметры должны соответствовать сигнатуре исходной функции
placeholders позволяют резервировать место для передачи параметров в вызове полученной функции
остальные подставляются в вызов исходной по значению

допустим есть функция int f(int, int, int)
при вызове auto f1=bind(f, _1, 1, 2); будет создан функциональный объект, принимающий 1 параметр int
и примерно равносильный объявлению
C++
1
2
3
4
int f(int, int, int);
int f1 (int _1) {
    return f(_1, 1, 2);
}
далее мы можем использовать f1 как обычную функцию с 1м параметром

с классами немного по другому: нужно помнить что передается скрытый параметр this, который компилятор приписывает в начало списка. т.е нужно указывать bind-у резервировать место под этот параметр, либо указывать с каким объектом производить связывание
1
42 / 42 / 17
Регистрация: 25.04.2014
Сообщений: 499
22.06.2015, 03:40  [ТС] 4
hoggy, не подскажете почему если написать
C++
1
2
3
4
5
pntr2(obj, i);
либо
pntr2(obj, i, j);
либо
pntr2(obj, i, j, z);
записи выдают одно и то же?

Добавлено через 9 минут
Cra3y,
при вызове auto f1=bind(f, _1, 1, 2);
_1 в вашем примере это не плейсхолдер?
0
шКодер самоучка
2127 / 1843 / 902
Регистрация: 09.10.2013
Сообщений: 4,029
Записей в блоге: 7
22.06.2015, 03:43 5
Цитата Сообщение от tapochka Посмотреть сообщение
_1 в вашем примере это не плейсхолдер?
он самый)
0
42 / 42 / 17
Регистрация: 25.04.2014
Сообщений: 499
22.06.2015, 04:15  [ТС] 6
Cra3y, можете, если не сложно, привести доходчивый пример где было бы несколько плейсхолдеров в одном байнде... не понятно пока что

Добавлено через 20 минут
нашел такой код:
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
void show(const string& a, const string& b, const string& c)
{
    cout << a << "; " << b << "; " << c << endl;
}
 
int main()
{
    auto x = bind(show, _1, _2, _3);
    auto y = bind(show, _3, _1, _2);
    auto z = bind(show, "hello", _2, _1);
     
    x("one", "two", "three");
    y("one", "two", "three");
    z("one", "two");
     
    return 0;
}
все работает на ура. Однако если написать так:
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
class CExample {
public:
    void show(const std::string& a, const std::string& b, const std::string& c)
    {
        std::cout << a << "; " << b << "; " << c << std::endl;
    }
};
 
int main() {
auto a1 = bind(&CExample::show, _1, _2, _3);
a1("one","two","three");
 
return 0;
}
то уже будет ошибка. как реализовать байнд в примере выше чтобы при вызове a1("one","two","three"); ошибки не было?
0
шКодер самоучка
2127 / 1843 / 902
Регистрация: 09.10.2013
Сообщений: 4,029
Записей в блоге: 7
22.06.2015, 04:23 7
Лучший ответ Сообщение было отмечено tapochka как решение

Решение

попробую...
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class test {
public:
    void some(std::fstream& out, int value) const {
        out << " called with " << this << ", " << value << std::endl;
    }
};
//....
using namespace std::placeholders; // _1, _2, etc.
test t;
auto f1 = std::bind(&test::some, _1, std::ref(std::cout), _2);
f1(t, 1); // -> t.some(std::cout, 1);
auto f2 = std::bind(&test::some, _1, _2, 2);
std::ofstream out("tmp.txt");
f2(t, out); // t.some(out, 2);
auto f3 = std::bind(&test::some, t, std::ref(out), 3); // передача t по значению, создается копия
f3(); // test t2 = t; t2.some(out, 3);
auto f4 = std::bind(&test::some, std::cref(t), std::ref(out), _1); // передача t по константной ссылке, копий нет
f4(4);// t.some(out, 4);
auto f5 = std::bind(&test::some, std::cref(t), _1, 5); // передача t по константной ссылке, копий нет
f5(std::cout);// t.some(std::cout, 5);
вместо placeholders при вызове подставляются переданные значения, на остальные места - значения переданные при bind

Добавлено через 6 минут
Цитата Сообщение от tapochka Посмотреть сообщение
чтобы при вызове a1("one","two","three"); ошибки не было?
Вы забываете о скрытом параметре this
в простейшем случае вам нужно передать в bind объект, с которым нужно вызывать эту функцию
C++
1
2
3
4
5
6
7
CExample obj;
// либо
auto a1 = bind(&CExample::show, std::ref(obj), _1, _2, _3);
a1( "1", "2", "3");
// либо
auto a2 = bind(&CExample::show, _1, _2, _3, _4);
a2(obj, "1", "2", "3");
1
42 / 42 / 17
Регистрация: 25.04.2014
Сообщений: 499
22.06.2015, 04:27  [ТС] 8
спасибо, буду разбираться долго еще видимо...

чуть-чуть прояснилось с найденным примером когда show в классе сделал статической
0
Эксперт С++
8391 / 3932 / 858
Регистрация: 15.11.2014
Сообщений: 8,869
22.06.2015, 10:41 9
Цитата Сообщение от tapochka Посмотреть сообщение
все работает на ура. Однако если написать так:
Цитата Сообщение от tapochka Посмотреть сообщение
то уже будет ошибка. как реализовать байнд в примере выше чтобы при вызове a1("one","two","three"); ошибки не было?
что бы запустить метод класса, нам нужен объект:
смотрите:

C++
1
2
auto f 
    = std::bind(свободная-функция, плейсхолдеры-или-аргументы-через-запятую);
C++
1
2
3
4
5
6
auto f 
    = std::bind(
        метод-класса,
        объект-ссылка-или-по-значению, 
        плейсхолдеры-или-аргументы-через-запятую
   );
в записи вида:
C++
1
auto pntr2 = std::bind(&CAmb::printt, std::placeholders::_1,  i, j, z);
&CAmb:rintt - метод класса,
значит следующим аргументом должен быть объект,
для которого нужно сделать запуск.

но там фигурирует плейсхолдер.
значит указать этот объект нужно при запуске функтора:

C++
1
2
3
4
5
6
CAmb obj;
    
auto pntr1 
    = std::bind(std::mem_fn(&CAmb::print), std::placeholders::_1, i, j);
 
pntr1(obj); //<--- при запуске указываем объект для которого нужно запустить метод
если бы это было вот так:
C++
1
2
3
4
5
6
7
8
CAmb obj;
auto pntr1 
    = std::bind(std::mem_fn(&CAmb::print), obj, std::placeholders::_1, j);
 
// объект уже указан (захват по значению), 
// а плейсхолдер приходится на один из аргументов запускаемой функции
 
pntr1(10); //<--- при запуске указываем недостающий аргумент i
если бы это было вот так:
C++
1
2
3
4
5
6
7
8
CAmb obj;
auto pntr1 
    = std::bind(std::mem_fn(&CAmb::print), std::placeholders::_1,std::placeholders::_2, j);
 
//плейсхолдеры приходятся и на объект
//и на аргумент i
 
pntr1(obj, 10); //<-- указываем объект, и недостающий аргумент.
std:laceholders::_1 приходится на объект для которого нужно запустить метод
std:laceholders::_2 на аргумент i.

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

C++
1
2
auto pntr3 = std::bind(print, i, j); //<--- нет прейсхолдеров. все аргументы забиндили
pntr3();
C++
1
2
3
//аргумент i нужно указать, аргумент j забиндили
auto pntr3  = std::bind(print,  std::placeholders::_1, j); 
pntr3(10);

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

разница только в том,
что для методов класса после указания самого метода,
идет указание объекта, для которого выполняется запуск.
1
42 / 42 / 17
Регистрация: 25.04.2014
Сообщений: 499
22.06.2015, 22:56  [ТС] 10
hoggy, а каково назначение std::mem_fn тут
C++
1
2
auto pntr1 
    = std::bind(std::mem_fn(&CAmb::print), std::placeholders::_1, i, j);
если прекрасно работает и без нее?
0
Эксперт С++
8391 / 3932 / 858
Регистрация: 15.11.2014
Сообщений: 8,869
22.06.2015, 22:57 11
Цитата Сообщение от tapochka Посмотреть сообщение
если прекрасно работает и без нее?
рудимент.
0
42 / 42 / 17
Регистрация: 25.04.2014
Сообщений: 499
23.06.2015, 00:44  [ТС] 12
hoggy, выходит она нужна только когда в алгоритм вставляем функцию член-класса? или есть еще какое-то применение?

Добавлено через 1 час 31 минуту
Cra3y, этот код
C++
1
2
3
4
5
6
7
// либо
CExample obj;
auto a1 = bind(&CExample::show, std::ref(obj), _1, _2, _3);
a1( "1", "2", "3");
// либо
auto a2 = bind(&CExample::show, _1, _2, _3, _4);
a2(obj, "1", "2", "3");
у меня не работает(VS2012): ошибки при вызове функторов - слишком много аргументов

Добавлено через 8 минут
извиняюсь... я же ее сам статической сделал и забыл
0
279 / 39 / 13
Регистрация: 11.10.2015
Сообщений: 405
05.03.2017, 17:24 13
hoggy, а вы случаем не знаете как через форич ввести данные в вектор пэйров?
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
struct lambda
{
    void input(std::istream& is, std::pair<int, int>& pp)
    {
        is >> pp.first >> pp.second;
    }
};
 
int main()
{
 
    std::vector<std::pair<int, int>> v(10);
    std::for_each(v.begin(), v.end(), std::bind(std::mem_fn(&lambda::input), std::placeholders::_1, std::ref(std::cin), std::make_pair(std::placeholders::_2, std::placeholders::_3)));
    for (auto& x : v)
        std::cout << x;
}
вот на что мозгов хватило...

Добавлено через 2 часа 1 минуту
hoggy, просто у меня уже закончились догадки в чем трабл...
C++
1
2
3
4
5
6
7
8
std::for_each(v.begin(), v.end(), 
        std::bind(
            std::mem_fn(&lambda::input), 
            std::placeholders::_1, 
            std::ref(std::cin), 
            std::bind(std::make_pair<int, int>, std::placeholders::_2, std::placeholders::_3)
        )
    );
по идее эта реализация верна...но компайл еррор
0
Эксперт С++
8391 / 3932 / 858
Регистрация: 15.11.2014
Сообщений: 8,869
05.03.2017, 17:32 14
Цитата Сообщение от zarko97 Посмотреть сообщение
вы случаем не знаете как через форич ввести данные в вектор пэйров?
http://rextester.com/CZQFI88809

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
#include <functional>
#include <algorithm>
#include <iostream>
#include <vector>
 
void input(std::istream& is, std::pair<int, int>& pp)
{
    is >> pp.first >> pp.second;
}
 
int main()
{
    using pair_t = std::pair<int, int>;
    using vec_t  = std::vector<pair_t>;
        
    vec_t v(2);
    
    // --- первый вариант с обычной лямбдой
    // что бы было видно, какой именно функтор
    // ожидает for_each
    const auto lambda = [](pair_t& el)
    {
        std::cin >> el.first >> el.second;
    };
    
    std::for_each(v.begin(), v.end(), lambda);
    
    
    
    // --- второй вариант с биндом
    const auto getter = std::bind(input, std::ref(std::cin), std::placeholders::_1);
    
    std::for_each(v.begin(), v.end(), getter);
    
    
    for (auto&& x : v)
        std::cout << "{" << x.first << ", " << x.second <<"}; " << std::endl;
}
1
279 / 39 / 13
Регистрация: 11.10.2015
Сообщений: 405
05.03.2017, 18:12 15
hoggy, хм...почему тогда не работает такая запись:
C++
1
2
3
4
5
6
7
8
std::vector<std::pair<int, int>> v(11);
    std::for_each(v.begin(), v.end(), 
std::bind(
            std::mem_fn(&lambda::input), 
            std::placeholders::_1,
            std::ref(std::cin), 
            std::placeholders::_2
        )
а такая работает;
C++
1
2
3
4
5
6
7
8
9
10
        lambda l;
    std::vector<std::pair<int, int>> v(11);
    std::for_each(v.begin(), v.end(), 
        std::bind(
            std::mem_fn(&lambda::input), 
            std::ref(l),
            std::ref(std::cin), 
            std::placeholders::_1
        )
    );
0
Эксперт С++
8391 / 3932 / 858
Регистрация: 15.11.2014
Сообщений: 8,869
05.03.2017, 20:38 16
Цитата Сообщение от zarko97 Посмотреть сообщение
почему тогда не работает такая запись
потому что вот это:

Цитата Сообщение от zarko97 Посмотреть сообщение
std::mem_fn(&lambda::input)
попытка забиндить функцию член класса.

нельзя вызывать методы классов без объектов.
это же не просто "свободные" функции.


std::bind для методов используется вот так:

auto functor = std::bind(имя-метода, объект, аргументы или плейсхолдеры);
1
IT_Exp
Эксперт
87844 / 49110 / 22898
Регистрация: 17.06.2006
Сообщений: 92,604
05.03.2017, 20:38

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

Передача std::bind в функцию
Ребята, помогите разобраться с 28 строкой. Глубоко не копал, но должно ведь компилиться :) ...

Std::bind с контролем типом
В общем, нужно реализовать аналог std::bind, который 1. Переводит void (*callback)(Args...) в...

Проверить объект в std::bind на живучесть
#include &lt;iostream&gt; #include &lt;string&gt; #include &lt;functional&gt; using namespace std; typedef...

std::bind и указатель на статическую функцию член класса
Биндинг простых функций класса-это понятно, а вот как быть, если функция статическая?


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

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

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