На главную Наши проекты:
Журнал   ·   Discuz!ML   ·   Wiki   ·   DRKB   ·   Помощь проекту
ПРАВИЛА FAQ Помощь Участники Календарь Избранное RSS
msm.ru
Модераторы: Qraizer, Hsilgos
Страницы: (3) [1] 2 3  все  ( Перейти к последнему сообщению )  
> Можно ли как-то без временных переменных?
    Всем привет!

    Накидал небольшой примерчик, и по нему есть вопрос:

    ExpandedWrap disabled
      #include <iostream>
      #include <functional>
       
      // ──────────────────────────────────────────────────────────────────────────────
       
      using CallbackType = std::function<void(void*)>;
       
      // ──────────────────────────────────────────────────────────────────────────────
       
      class Karbofos {
        
          CallbackType *Begin = nullptr;
          CallbackType *End = nullptr;
        
        public:
        
          const std::string Name = "Карбофос";
          
          void SetCallback(CallbackType *B, CallbackType *E) {
            Begin = B;
            End = E;
          }
          
          void Run() {
            try {
              if (Begin) (*Begin)(this);
              std::cout << "- Не отдам, слон - мой!\n";
              if (End) (*End)(this);
            } catch(const std::bad_function_call& E) {
              std::cout << "Хреновый вызов: " << E.what() << '\n';
            } catch(...) {
              std::cout << "Случилось страшное!\n";
            }
          }
          
      };
       
      // ──────────────────────────────────────────────────────────────────────────────
       
      void Shef(void* i) {
        std::cout << "- Стой, живодер " << ((Karbofos*)(i))->Name << "!\n";
      }
       
      // ──────────────────────────────────────────────────────────────────────────────
       
      void Kollega(void* i) {
        std::cout << "- " << ((Karbofos*)(i))->Name << ", ты немец и контрабандист!\n";
      }
       
      // ──────────────────────────────────────────────────────────────────────────────
       
      int main() {
        Karbofos Object;  
        CallbackType S = Shef;
        CallbackType K = Kollega;
        Object.SetCallback(&S,&K);
        Object.Run();
        return 0;
      }

    Как мне в SetCallback передать сразу указатели на функции, без использования временных переменных?

    Добавлено
    Блин, переработался я :lol: new CallbackType(), решает ... осталось как-то возможную утечку памяти подпилить хитрыми указателями.
      На SO залепили на умных указателях. Ну выход. Ну почему просто так нельзя как-то?
        Цитата JoeUser @
        Как мне в SetCallback передать сразу указатели на функции, без использования временных переменных?

        У тебя прям талант придумывать себе проблемы. Убери указатели вообще у CallbackType. Зачем ты их туда написал? С какой целью ты их туда написал? Сам CallbackType - не является разве указателем(ну вернее он является std::function, но сам шаблонный параметр то указатель на функцию)?
        Ты у себя прилепил указатель на указатель на функцию и спрашиваешь такие вещи.

        https://ideone.com/miVYwD
        ExpandedWrap disabled
              #include <iostream>
              #include <functional>
              
              // ──────────────────────────────────────────────────────────────────────────────
              
              using CallbackType = std::function<void(void*)>;
              
              // ──────────────────────────────────────────────────────────────────────────────
              
              class Karbofos {
              
                  CallbackType Begin = nullptr;
                  CallbackType End = nullptr;
              
                public:
              
                  const std::string Name = "Карбофос";
              
                  void SetCallback(CallbackType B, CallbackType E) {
                    Begin = B;
                    End = E;
                  }
              
                  void Run() {
                    if (Begin) (Begin)(this);
                    std::cout << "- Не отдам, слон - мой!\n";
                    if (End) (End)(this);
                  }
              
              };
              
              // ──────────────────────────────────────────────────────────────────────────────
              
              void Shef(void* i) {
                std::cout << "- Стой, живодер " << ((Karbofos*)(i))->Name << "!\n";
              }
              
              // ──────────────────────────────────────────────────────────────────────────────
              
              void Kollega(void* i) {
                std::cout << "- " << ((Karbofos*)(i))->Name << ", ты немец и контрабандист!\n";
              }
              
              // ──────────────────────────────────────────────────────────────────────────────
              
              int main() {
                Karbofos Object;  
                //CallbackType S = Shef;
                //CallbackType K = Kollega;
                Object.SetCallback(Shef,Kollega);
                Object.Run();
                return 0;
              }


        Добавлено
        Цитата JoeUser @
        На SO залепили на умных указателях. Ну выход. Ну почему просто так нельзя как-то?

        Ну там днари походу сидят, дергают на свой рейтинг и пишут всякий оцтой. Раньше там чаще можно было встретить полезной информации. Сейчас приходится перелопачивать тонну какого то шлака, чтоб найти вменяемый ответ.

        Добавлено
        Цитата JoeUser @
        Блин, переработался я :lol: new CallbackType(), решает ... осталось как-то возможную утечку памяти подпилить хитрыми указателями.

        "...Таких извращений Иван не видал!..." © Красная Плесень.
        Сообщение отредактировано: Wound -
          Киля, ну прости! :lol: Спасибо.

          Добавлено
          Кстати, есть еще совет вот такой:
          ExpandedWrap disabled
            void SetCallback(CallbackType B, CallbackType E) {
              Begin = std::move(B);
              End = std::move(E);
            }

          На сколько тут std::move нужен?
            Цитата JoeUser @
            На сколько тут std::move нужен?

            Без понятия, тонкости реализации я уже забыл, это пусть лучше Qraizer просветлит.
            Но как по мне - он там нахрен не нужен.
            Во первых, чтоб применять move - Нужен rvalue, т.е. функция должна принимать что то типа:
            ExpandedWrap disabled
                  void SetCallback(CallbackType&& B, CallbackType&& E) {
                    Begin = std::move(B);
                    End = std::move(E);
                  }

            Во вторых, даже если все будет так - никаких гарантий нет что будет реализовано перемещение.
            В третьих, даже если ты явно не напишешь std::move, а по логике он там применим, компилятор сам выполнит перемещение(соптимизирует и выполнит)
            В четвертых - у тебя там не просто указатели на какие то переменные, а указатели на функцию, не думаю что оно там будет что то перемещать, хотя ньюансы не знаю.

            ИМХО - это лишнее.
              Лан, ждем Qraizer8-)
                Ну и к слову вопрос еще состоит в том - можно ли перемещать указатели? У меня стойкое ощущение что указатель - это lvalue, а никак не rvalue.
                Майерса год-два назад читал, там про это все написано.

                Добавлено
                Как мне помнится
                lvalue - это то, что живет на следующей строчке, после его объявления.
                rvalue - это то, что не живет на следующей строчке, после его объявления.

                Что то типа там:
                ExpandedWrap disabled
                  int a = 5; //! a = lvalue, 5 - rvalue
                  SomeClass b = a.getSomeClass(); //! b - lvalue, a.getSomeClass() - rvalue(если возвращается копия объекта, а не какой нибудь кусок памяти в куче).
                  void SomeFunc(std::string str); //! str - rvalue, потому что живет не дольше SomeFunc
                  void SomeFunc2(const std::string& str)//! str - lvalue, потому что живет дольше SomeFunc

                Это я так по памяти воспроизвел. Значит и указатель на функцию это 100% lvalue, и std::move, просто скопирует побайтно объект, никаких семантик перемещения не будет.

                Добавлено
                Разве что у тебя там std::function сможет переместится, что то я его проглядел, но это уже надо его конструкторы смотреть - если есть перемещающий, возможно и можно.
                Сообщение отредактировано: Wound -
                  Wound, я бегло глянул в сети, не вдавался в подробности. Пишут, что std::move как-то связан со списками захвата, если в качестве функции передается лямбда.

                  И вааще, Киля, ты должен оценить сейчас всю глубину глубин :lol:

                  user posted image
                    Цитата JoeUser @
                    Пишут, что std::move как-то связан со списками захвата, если в качестве функции передается лямбда.

                    Ну тот код который привел ты - у тебя там нет лямбд :-?
                      Цитата Wound @
                      Ну тот код который привел ты - у тебя там нет лямбд

                      За-то я написал "если" :)
                        Цитата JoeUser @
                        За-то я написал "если"

                        Ну сам std::move вообще не занимается ничем, кроме каста входящего значения в rvalue, так что вряд ли он там может быть с чем то связан. Вот например можно посмотреть псевдро-реализацию std::move в блоге Майерса, ну или почитать его книжку, там об этом написано.
                        http://scottmeyers.blogspot.com/2012/11/on...of-stdmove.html
                          Начнём с того, что функциональные объекты придумали как раз для того, чтобы добавить к "фрагменту кода" семантику значений и тем самым сделать работу с ним подобной работе с данными: запомнил, передал, переприсвоил, "частичные запомнил" аргументы, ...использовал запомненное, в смысле, вызвал. Лепить к ним ещё и указатели чаще всего бессмысленно, не реже, чем через поинтеры работать с обычными переменными, что лежат не в dynamic storage.
                          Продолжим тем, что ежели функция принимает неконстанту иначе, нежели по значению, это означает контракт, при котором она будет его менять, т.е. через него возвращается результат её работы. Поэтому Стандарт запрещает передавать rvalue под такие параметры, ибо они, являясь временными объектами, по возврату из функции просто не дают возможности этими результатами воспользоваться, ибо они будут разрушены на ближайшей ; (ну, почти всегда на ближайшей), а значит программист практически наверняка облажался, написав такую конструкцию.
                          Цитата JoeUser @
                          На сколько тут std::move нужен?

                          Цитата Wound @
                          Но как по мне - он там нахрен не нужен.
                          Во первых, чтоб применять move - Нужен rvalue, ...
                          Не спорю, запутаться с семантикой ссылок недолго. Сам путался, пока не привык. Но это несложно, не сложнее, чем разобраться с ссылками после C, где их не было.
                          Вы путаете причину и следствие. Немного ликбеза. std::move() нужен, чтобы ...скажем так, преобразовать (не правильный глагол на самом деле) объект к rvalue ref, изначально таковым не являвшимся. Нужно это конечно же для того, чтобы компилятор в этой точке смог использовать семантику перемещения, если сможет, ибо в противном случае он предпочтёт другие методы. Связано это с тем, что (и снова не вполне правильная терминология) перемещения обладают низким приоритетом при наличии других возможностей. А это в свою очередь связано с тем, что семантика перемещения подразумевает разрушение своего источника, что естественно весьма опасно для невременных объектов, поэтому надёжнее предположить, что "программист практически наверняка облажался, написав такую конструкцию" ©. Используя std::move(), программист чётко даёт понять компилятору, что он не буратина, и знает, что делает. Другой причины использовать std::move() нет. В частности его бессмысленно применять к объектам, которые и так уже rvalue ref.
                          И тут есть одна ловушка: rvalue ref – это просто контракт, срабатывающий при инициализации, который разрешает биндить ссылки на временные объекты, что иначе запрещено контрактом обычных ссылок по причине, как выше было аргументировано. Но в остальном это самые что ни на есть обычные ссылки, которые, как известно являются псевдонимами своих подссыльних объектов. Разница между этими типами ссылок в том, что обычная lvalue ссылка является псевдонимом lvalue, а rvalue ref псевдонимом rvalue. Это приводит к тому, что после инициализации любое использование lvalue ref сводится к lvalue, что практически не привносит в дальнейшем особых сложностей в семантику использования получившегося в итоге объекта, тогда как использование rvalue ref сводится к rvalue, что в связи с вышеозначенным низким приоритетом бинда rvalue к ссылочной семантике, очень часто приводит к необходимости постоянно повторять компилятору, что мы не буратина.
                          Поэтому, Wound, ты прав в том смысле, что std::move не помешает, чтобы компилятор смог использовать семантику перемещения в присваиваниях Begin и End, но для этого источникам нет необходимости быть обязательно rvalue ref, так что параметры SetCallback() могут быть и обычными значениями. Другое дело, что не объявив их rvalue ref, сам вызов SetCallback() при связывании аргументов с его параметрами не заюзает перемещения, аргументы в параметры скопируются, так что да, имеет смысл идти до конца и их также объявить как rvalue ref. Однако это уже не так однозначно, так как в результате контракт допускает, что при вызове SetCallback() её аргументы могут быть также разрушены в точке инициализации её параметров, а не только эти параметры в присваиваниях Begin и End, тогда как если параметры SetCallback() суть простые значения, подобного с аргументами не произойдёт: параметры, будучи их копиями, могут быть разрушены после перемещения Begin и End, т.к. к rvalue ref будут сведены они, т.е. разрушатся только лишь копии аргументов.
                          Что же касается
                          Цитата Wound @
                          В третьих, даже если ты явно не напишешь std::move, а по логике он там применим, компилятор сам выполнит перемещение(соптимизирует и выполнит)
                          то как я чуть выше сказал, компилятор сам этого не сделает, для него использование rvalue ref порождает новые rvalue объекты, которые сводить обратно к rvalue ref нужно заново. Точнее, иногда он всё-таки будет способен на подобное, но в очень редких случаях, например, если источником уже является некая несводимая rvalue ref, например возвращаемое значение некой функции, которая возвращает результат по rvalue ref.
                          Цитата Wound @
                          Во вторых, даже если все будет так - никаких гарантий нет что будет реализовано перемещение.
                          Ну это классика. Семантика перемещения для стандартных типов не описывает, что происходит с источниками, однако подавляющее большинство реализаций будут просто их копировать, так что никакой разницы от копирования нет. Но это не точно, Стандарт ничего тут не гарантирует, просто реализациям так проще. Что касается перемещений не стандартных типов, то тут всё в руках программиста, написавшего (или забившего на это) код перемещения.

                          Добавлено
                          Вот маленький пример для иллюстрации сказанного
                          ExpandedWrap disabled
                            int b;
                             
                            int&& f(int&& z)
                            {
                              b = std::move(z);             // без std::move() скопилится, но и не разрушит z
                             
                            //  return z;                   не скопилится, нужен каст из rvalue к rvalue ref
                              return std::move(z);
                            }
                             
                            void g(int&& z)
                            {
                              // первый (внутренний) вызов без std::move() не скопилится, нужен каст из rvalue к rvalue ref
                              // второй (внешний) вызов std::move() не требует, т.к. это несводимый контекст использования ссылок
                              f(f(std::move(z)));
                            }
                          Сообщение отредактировано: Qraizer -
                            Qraizer, так в коде:

                            ExpandedWrap disabled
                              void SetCallback(CallbackType B, CallbackType E) {
                                Begin = std::move(B);
                                End = std::move(E);
                              }

                            Все-таки std::move нужен, иначе будут копирования?
                              Угу. Даже если там был бы только перемещающий operator=(), компилятор сам rvalue ref из rvalue не сделает, остановится с ошибкой.

                              Добавлено
                              С другой стороны копирование std::function без захваченных неPOD дешёвое, так что много не потеряешь.
                                Цитата Wound @
                                Во первых, чтоб применять move - Нужен rvalue, т.е. функция должна принимать что то типа:

                                Не обязательно.
                                ExpandedWrap disabled
                                  void MyClass::set_something(std::vector<Something> value)  // Передача по значению
                                  {
                                      this->something = std::move(value); // Переместит, лишних копирований не будет
                                  }


                                Цитата Qraizer @
                                Поэтому Стандарт запрещает передавать rvalue под такие параметры, ибо они, являясь временными объектами, по возврату из функции просто не дают возможности этими результатами воспользоваться, ибо они будут разрушены на ближайшей ; (ну, почти всегда на ближайшей), а значит программист практически наверняка облажался, написав такую конструкцию.

                                По мне так это просто недосмотр языка. По-моему не появится никаких проблем, если разрешить передавать rvalue в параметры-неконстантные ссылки, которых бы не было в случае параметров-константных ссылок.
                                0 пользователей читают эту тему (0 гостей и 0 скрытых пользователей)
                                0 пользователей:
                                Страницы: (3) [1] 2 3  все


                                Рейтинг@Mail.ru
                                [ Script execution time: 0,0868 ]   [ 17 queries used ]   [ Generated: 19.04.24, 00:39 GMT ]