Гость
Целевая тема:
Создать новую тему:
Автор:
Форумы / C++ [игнор отключен] [закрыт для гостей] / Не контачат std::function и std::bind / 24 сообщений из 24, страница 1 из 1
03.06.2018, 16:55
    #39654520
AlekseySQL
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Не контачат std::function и std::bind
Написал такой простой код:

main.cpp:
Код: plaintext
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.
#include <functional>
#include <iostream>

namespace
{
    template<typename Type, bool NeedLock = false>
    class MyClass
    {
        Type* p;
    public:

        //disable copying and assignment
        MyClass():
            p(nullptr)
        {}

        MyClass(const MyClass<Type, NeedLock>& Value) = delete;
        MyClass& operator=(const MyClass<Type, NeedLock>& Value) = delete;
        ~MyClass() = default;

        MyClass(MyClass<Type, NeedLock>&& Value):
            p(Value.p)
        {
            Value.p = nullptr;
        }

        MyClass& operator = (MyClass&& Value) noexcept
        {
            p = Value.p;
            Value.p = nullptr;

            return *this;
        }
    };

    template<typename Type, bool NeedLock = false>
    void Write(MyClass<Type, NeedLock>&& foo, string& Data)
    {
        if(NeedLock) std::cout << "Hello, world!" << std::endl;
    }

    template<typename Type, bool NeedLock = false>
    void BigWrite(MyClass<Type, NeedLock>& foo)
    {
        string Data = "fdfdsfdsf";
        std::function<void(void)> Bind = std::bind(&Write<Type, NeedLock>, std::move(foo), Data); //ОШИБКА!!!
    }
}

int main(int argc, char *argv[])
{
    MyClass<int> foo;
    BigWrite<int>(foo);
}



авторошибка: conversion from ‘std::_Bind_helper<false, void (*)({anonymous}::MyClass<int>&&, std::__cxx11::basic_string<char>&), {anonymous}::MyClass<int, false>, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >&>::type {aka std::_Bind<void (*({anonymous}::MyClass<int>, std::__cxx11::basic_string<char>))({anonymous}::MyClass<int>&&, std::__cxx11::basic_string<char>&)>}’ to non-scalar type ‘std::function<void()>’ requested
std::function<void(void)> Bind = std::bind(&Write<Type, NeedLock>, std::move(foo), Data);
^~~~

В чем проблема?
...
Рейтинг: 0 / 0
03.06.2018, 18:14
    #39654537
Anatoly Moskovsky
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Не контачат std::function и std::bind
AlekseySQL,

Наверно bind не умеет передавать rv-ссылки && как есть, и где-то внутри заменяет их на & или вообще на копии объекта.
Замените && на &, а std::move в строке с bind на std::ref. (И для Data тоже надо std::ref, кстати, либо убрать ссылку из типа аргумента)
Типа этого:
Код: plaintext
1.
std::bind(&Write<Type, NeedLock>, std::ref(foo), std::ref(Data))


А если вам надо именно перемещать foo то используте shared_ptr

PS. Думаю что и с rv-ссылками можно извратиться и придумать как, но зачем...
...
Рейтинг: 0 / 0
03.06.2018, 18:27
    #39654539
AlekseySQL
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Не контачат std::function и std::bind
Anatoly MoskovskyAlekseySQL,

Наверно bind не умеет передавать rv-ссылки && как есть, и где-то внутри заменяет их на & или вообще на копии объекта.
Замените && на &, а std::move в строке с bind на std::ref. (И для Data тоже надо std::ref, кстати, либо убрать ссылку из типа аргумента)
Типа этого:
Код: plaintext
1.
std::bind(&Write<Type, NeedLock>, std::ref(foo), std::ref(Data))


А если вам надо именно перемещать foo то используте shared_ptr

PS. Думаю что и с rv-ссылками можно извратиться и придумать как, но зачем...

Документация говорит другое:
авторАргументы для связывания будут скопированы или перемещены, и никогда не передаются по ссылке, если только не завернуты в std::ref или std::cref.

К тому же у меня уже есть рабочий код, в котором bind принимает перемещенный unique_ptr (я им обернул iostream, чтобы при возникновении исключения файл корректно закрылся).
...
Рейтинг: 0 / 0
04.06.2018, 10:19
    #39654714
OoCc
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Не контачат std::function и std::bind
AlekseySQLAnatoly MoskovskyAlekseySQL,

Наверно bind не умеет передавать rv-ссылки && как есть, и где-то внутри заменяет их на & или вообще на копии объекта.
Замените && на &, а std::move в строке с bind на std::ref. (И для Data тоже надо std::ref, кстати, либо убрать ссылку из типа аргумента)
Типа этого:
Код: plaintext
1.
std::bind(&Write<Type, NeedLock>, std::ref(foo), std::ref(Data))


А если вам надо именно перемещать foo то используте shared_ptr

PS. Думаю что и с rv-ссылками можно извратиться и придумать как, но зачем...

Документация говорит другое:
авторАргументы для связывания будут скопированы или перемещены, и никогда не передаются по ссылке, если только не завернуты в std::ref или std::cref.

К тому же у меня уже есть рабочий код, в котором bind принимает перемещенный unique_ptr (я им обернул iostream, чтобы при возникновении исключения файл корректно закрылся).
bind обьект может вызываться больше одного раза. Поэтому передача ему rv аргумента бессмысленна. Потому он их и не берёт. Ты можешь сконструировать bind обьект с rv аргументом но не сможешь его исполнить по причине описанной в первом предложении.

В твоём "работающем" коде нет исполнения bind обьекта с rv аргументом.
...
Рейтинг: 0 / 0
04.06.2018, 10:20
    #39654716
AlekseySQL
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Не контачат std::function и std::bind
Anatoly Moskovsky,
К сожалению, использовать ссылки не получится: у меня происходит итерация по циклу с многократным заполнением / освобождением вспомогательного массива, который через бинды передается для асинхронной записи на диск. Если я передам ссылку на обертку массива, то перейдя к записям нового клиента я очищу массив и заполню его новыми данными, т.е. по этой ссылке будут лежать уже другие записи...

Так что нужно либо передавать по значению (что очень плохо при работе с массивами), либо перемещать...
...
Рейтинг: 0 / 0
04.06.2018, 10:39
    #39654724
AlekseySQL
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Не контачат std::function и std::bind
OoCcbind обьект может вызываться больше одного раза. Поэтому передача ему rv аргумента бессмысленна. Потому он их и не берёт. Ты можешь сконструировать bind обьект с rv аргументом но не сможешь его исполнить по причине описанной в первом предложении.

В твоём "работающем" коде нет исполнения bind обьекта с rv аргументом.

Возможно вы правы. Заглянул в свой "работающий" код:
Код: plaintext
1.
2.
3.
4.
5.
6.
void AsyncExecuteDeleteProcedureOfUniquePtr(const variativeData& Settings,
                                                uptr_Type&& File)
{
std::function<void(void)>&& Bind = std::bind(File.get_deleter(), File.release());
...
}



Другими словами из unique_ptr я выдергивал сырой указатель и функцию удаления объекта и именно их сохранял в бинде. Так сказать, "раздербанивал" unique_ptr для его помещения в бинд.

Хотя мне этот подход кажется неправильным: программист для того и нужен, чтобы следить за логикой работы, и если появится ошибка повторного перемещения объекта, то за разбирательства в таких ошибках он и получает свой хлеб.
...
Рейтинг: 0 / 0
04.06.2018, 11:01
    #39654732
AlekseySQL
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Не контачат std::function и std::bind
Короче, я запутался в двух деревьях: надо сначала переместить свой массив в функцию (принимая НЕ по правой ссылке), а при создании bind запихнуть либо по значению (поскольку мой объект хранит указатель на выделенную память и весит очень мало), либо использовать cref. Я упростил предыдущий вариант, убрав шаблоны:

Код: plaintext
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.
#include <functional>
#include <iostream>
namespace
{
    class MyClass
    {
        int* p;
    public:

        //disable copying and assignment
        MyClass():
            p(nullptr)
        {}

        MyClass(const MyClass& Value) = delete;
        MyClass& operator=(const MyClass& Value) = delete;
        ~MyClass() = default;

        MyClass(MyClass&& Value):
            p(Value.p)
        {
            Value.p = nullptr;
        }

        MyClass& operator = (MyClass&& Value) noexcept
        {
            p = Value.p;
            Value.p = nullptr;

            return *this;
        }
    };

    void Write(const MyClass& foo) //приняли foo по ссылке
    {
        std::cout << "Hello, world!" << std::endl;
    }

    void BigWrite(MyClass foo) //тут foo содержит валидный указатель
    {
        std::function<void(void)> Bind = std::bind(&Write, std::cref(foo));
        Bind();
    }
}

int main(int argc, char *argv[])
{
    MyClass foo;
    BigWrite(std::move(foo)); //теперь здесь foo пустой и его можно опять наполнять данными
}
...
Рейтинг: 0 / 0
06.06.2018, 09:55
    #39656147
AlekseySQL
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Не контачат std::function и std::bind
Продолжу свой диалог :)

В последнем примере допущена логическая ошибка: когда я выхожу из функции BigWrite, то локальная переменная foo уничтожается, и следовательно в bind- е лежит ссылка на мусор. Конечно, в приведенном примере bind до выхода из функции вызывает оператор () и ошибки вроде как нет, но если он будет использоваться для асинхронного выполнения действий (и класться в какую- нибудь коллекцию для последующего выполнения), то подобный подход не сработает. Поэтому в bind надо класть само значение, а не ссылку (не зря разработчики в bind все принимают по значению).
...
Рейтинг: 0 / 0
06.06.2018, 10:24
    #39656166
AlekseySQL
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Не контачат std::function и std::bind
Переделал для сохранения в bind-е самого значения, но получаю ошибку (MyClass не изменен):

Код: plaintext
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.
namespace
{
    class MyClass
    {
        int* p;
    public:

        //disable copying and assignment
        MyClass():
            p(nullptr)
        {}

        MyClass(const MyClass& Value) = delete;
        MyClass& operator=(const MyClass& Value) = delete;
        ~MyClass() = default;

        MyClass(MyClass&& Value):
            p(Value.p)
        {
            Value.p = nullptr;
        }

        MyClass& operator = (MyClass&& Value) noexcept
        {
            p = Value.p;
            Value.p = nullptr;

            return *this;
        }
    };

void Write(const MyClass& foo) //приняли foo по ссылке
    {
        std::cout << "Hello, world!" << std::endl;
    }

    void BigWrite(const MyClass& foo) //тут foo содержит валидный указатель
    {
        std::function<void(void)> Bind = std::bind(&Write, foo); //ОШИБКА!!!
        //Bind();
    }
}

int main(int argc, char *argv[])
{
    MyClass foo;
    BigWrite(foo);
}



автор/usr/include/c++/7/functional:534: ошибка: no matching function for call to ‘std::tuple<{anonymous}::MyClass>::tuple({anonymous}::MyClass&)’
: _M_f(std::move(__f)), _M_bound_args(std::forward<_Args>(__args)...)
^

Как лечить?
...
Рейтинг: 0 / 0
06.06.2018, 13:28
    #39656443
AlekseySQL
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Не контачат std::function и std::bind
Выяснил, что проблема в отсутствии конструктора копирования :)

Надо так:
Код: plaintext
1.
 MyClass(const MyClass<Type, NeedLock>& Value) = default;



Но меня все- таки гложат сомнения: неужели никак нельзя в bind переместить объект? Неужели обязательно создавать копию?
...
Рейтинг: 0 / 0
06.06.2018, 13:51
    #39656476
Anatoly Moskovsky
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Не контачат std::function и std::bind
AlekseySQLНо меня все- таки гложат сомнения: неужели никак нельзя в bind переместить объект? Неужели обязательно создавать копию?
Начиная с C++14 можно.
Но не через bind, а через лямбду.

Код: plaintext
1.
2.
3.
std::function<void(void)> Bind = [foo{std::move(foo)}](){
    Write(std::move(foo));
};


Полученный объект будет movable

Гуглите "Generalized lambda capture".

PS. В C++11 тоже можно извратиться в большинстве случаев. Для этого надо написать обертку которая реализует семантику перемещения в конструкторе копирования. Но тут конечно надо аккуратно ))
...
Рейтинг: 0 / 0
06.06.2018, 14:49
    #39656550
AlekseySQL
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Не контачат std::function и std::bind
Anatoly Moskovsky, спасибо!
...
Рейтинг: 0 / 0
13.06.2018, 09:19
    #39660030
AlekseySQL
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Не контачат std::function и std::bind
OoCcbind обьект может вызываться больше одного раза. Поэтому передача ему rv аргумента бессмысленна. Потому он их и не берёт. Ты можешь сконструировать bind обьект с rv аргументом но не сможешь его исполнить по причине описанной в первом предложении


У Скота Майерса ("Эффективный и современный С++") на 232 странице он в примере перемещает данные в bind, а в последнем абзаце говорится, что при этом в bind создается lvalue- копия перемещенного объекта, которую можно использовать многократно. Короче, я так и не понял почему везде написано, что в bind перемещать можно, а по факту- ошибка.
...
Рейтинг: 0 / 0
13.06.2018, 12:01
    #39660170
NekZ
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Не контачат std::function и std::bind
AlekseySQL,

А ещё Мейерс советует не пользоваться bind'ом, отдавая предпочтение лямбдам.
...
Рейтинг: 0 / 0
13.06.2018, 16:55
    #39660396
Anatoly Moskovsky
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Не контачат std::function и std::bind
AlekseySQLв последнем абзаце говорится, что при этом в bind создается lvalue- копия перемещенного объекта, которую можно использовать многократно. Короче, я так и не понял почему везде написано, что в bind перемещать можно, а по факту- ошибка.
lvalue- копия возможна только если у вас разрешено копирование ))
...
Рейтинг: 0 / 0
13.06.2018, 16:59
    #39660399
Anatoly Moskovsky
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Не контачат std::function и std::bind
Точнее bind вы сделаете, но скопировать его куда-то не сможете.
Но вызвать не копируя сможете.
...
Рейтинг: 0 / 0
13.06.2018, 19:10
    #39660471
AlekseySQL
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Не контачат std::function и std::bind
Anatoly MoskovskyТочнее bind вы сделаете, но скопировать его куда-то не сможете.
Но вызвать не копируя сможете.

Спасибо, за помощь в поисках. Но ошибка вылазит на строчке создания bind. В последующем (ниже по коду) я этот бинд с помощью std::move перемещаю в очередь для асинхронного выполнения отдельным потоком (не копирую, а именно перемещаю). Так что вроде ваша мысль мимо цели.
...
Рейтинг: 0 / 0
13.06.2018, 19:48
    #39660480
Anatoly Moskovsky
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Не контачат std::function и std::bind
AlekseySQL,

Потому что тут копирование:
Код: plaintext
1.
std::function<void(void)> Bind = std::bind ...


А вот ту нет:
Код: plaintext
1.
auto Bind = std::function<void(void)> Bind = std::bind
...
Рейтинг: 0 / 0
13.06.2018, 19:49
    #39660481
Anatoly Moskovsky
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Не контачат std::function и std::bind
Опечаточка была.

А вот тут нет:
Код: plaintext
1.
auto Bind = Bind = std::bind...
...
Рейтинг: 0 / 0
13.06.2018, 19:50
    #39660482
Anatoly Moskovsky
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Не контачат std::function и std::bind
Последняя попытка

А вот тут нет:
Код: plaintext
1.
auto Bind = std::bind...
...
Рейтинг: 0 / 0
15.06.2018, 09:10
    #39661067
AlekseySQL
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Не контачат std::function и std::bind
Anatoly Moskovsky, а можно подробнее, почему тут
есть копирование:
Код: plaintext
1.
std::function<void(void)> Bind = std::bind ...



, а тут нет ???:
Код: plaintext
1.
auto Bind = std::bind...



Если бы спецификатор auto мог содержать в себе знак ссылки или временной ссылки, то я бы понял, но ведь он содержит только тип... Совсем не понятно.
...
Рейтинг: 0 / 0
15.06.2018, 10:05
    #39661110
AlekseySQL
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Не контачат std::function и std::bind
Решил выяснить какие же функции вызываются и написал:

Код: plaintext
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.
namespace
{
    class MyClass
    {
        int* p;
    public:

        //disable copying and assignment
        MyClass():
            p(nullptr)
        {
            std::cout << "Create" << std::endl;
        }

        MyClass(const MyClass& Value)
        {
            std::cout << "Copy" << std::endl;
        }

        MyClass& operator=(const MyClass& Value)
        {
             std::cout << "Assignment" << std::endl;
        }

        ~MyClass()
        {
            std::cout << "Destructor" << std::endl;
        }

        MyClass(MyClass&& Value):
            p(Value.p)
        {
            Value.p = nullptr;
            std::cout << "Moving copy" << std::endl;
        }

        MyClass& operator = (MyClass&& Value) noexcept
        {
            p = Value.p;
            Value.p = nullptr;
            std::cout << "Moving assignment" << std::endl;

            return *this;
        }
    };

    void Write(const MyClass& foo)
    {
        std::cout << "Hello, world!" << std::endl;
    }

    void BigWrite(const MyClass& foo) //В КОММЕНТАРИЯХ УКАЗАЛ ВЫВОД
    {
        auto Bind = std::bind(&Write, foo); //Copy
        auto Bind2 = std::bind(&Write, std::move(foo)); //Copy
        std::function<void(void)> Bind3 = std::bind(&Write, foo); //Copy, Moving copy, Destructor
        std::function<void(void)> Bind4 = std::bind(&Write, std::move(foo)); //Copy, Moving copy, Destructor

        Bind(); //Hello, world!

    }//+Destructor на каждый созданный Bind
}

int main(int argc, char *argv[])
{
    MyClass foo; //Create
    BigWrite(foo);
    return 1;
}



Почему спецификатор auto так меняет положение вещей?
...
Рейтинг: 0 / 0
15.06.2018, 10:49
    #39661136
AlekseySQL
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Не контачат std::function и std::bind
Решил продолжить эксперименты и получил еще более адов вывод:

Код: plaintext
1.
2.
3.
4.
5.
6.
7.
void BigWrite(const MyClass& foo)
{
auto&& Bind = std::move(std::bind(&Write, foo)); //Copy, Destructor
auto Bind2 = std::move(std::bind(&Write, foo)); //Copy, Moving copy, Destructor
std::function<void(void)> Bind3 = std::move(std::bind(&Write, foo)); //Copy, Moving copy, Moving copy, Destructor, Destructor
std::function<void(void)>&& Bind4 = std::move(std::bind(&Write, foo)); //Copy, Moving copy, Moving copy, Destructor, Destructor
}



В первой строчке вызывается деструктор (!!!), и если после этой строки написать Bind(), то в функции Write вместо наполненного объекта foo, мы получаем пустой объект... Похоже разобраться cо странным объектом bind не получится и надо просто уходить на лямбды ...
...
Рейтинг: 0 / 0
15.06.2018, 12:56
    #39661230
AlekseySQL
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Не контачат std::function и std::bind
А вот лямбда- функции работают хорошо (у меня есть пул задач, у которых есть поле std::function<void(void)>):

Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
         std::cout << "___________________________________" << std::endl;
         auto Bind5 = [foo = std::move(foo)]
        {
            Write(foo);
        };

        asyncTask* const CurrentTask5 = new asyncTask(Settings, std::move(Bind5));
        AsyncPool->AddTask(CurrentTask5);
        CurrentTask5->operator ()();
        std::cout << "___________________________________" << std::endl;

        std::function<void(void)> Bind6 = [foo = std::move(foo2)]
        {
            Write(foo);
        };

        asyncTask* const CurrentTask6 = new asyncTask(Settings, std::move(Bind6));
        AsyncPool->AddTask(CurrentTask6);
        CurrentTask6->operator ()();
         std::cout << "___________________________________" << std::endl;




Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
___________________________________
Moving copy
Moving copy
Moving copy
Destructor
Hello, world!
___________________________________
Moving copy
Moving copy
Destructor
Hello, world!
___________________________________

Видно, что если Bind объявлять с помощью std::function<void(void)>, то количество перемещений сокращается.
...
Рейтинг: 0 / 0
Форумы / C++ [игнор отключен] [закрыт для гостей] / Не контачат std::function и std::bind / 24 сообщений из 24, страница 1 из 1
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


Просмотр
0 / 0
Close
Debug Console [Select Text]