На главную Наши проекты:
Журнал   ·   Discuz!ML   ·   Wiki   ·   DRKB   ·   Помощь проекту
ПРАВИЛА FAQ Помощь Участники Календарь Избранное RSS
msm.ru
Модераторы: Qraizer, Hsilgos
  
> Аналог std::function , Реализация класса типа std::function, с movable-параметрами
    Известно, что для std::function обязательно требуется конструктор копирования.
    Однако, довольно регулярно требуется во всякие callback'и и потоки передавать non-copyable-объекты, типа std::unique_ptr, std::unique_lock и т.д.
    В свою очередь, std::bind возвращает вполне себе нормальный объект (не знаю какой), которому спокойно можно делать move.
    Поэтому решил написать обёртку для этого объекта:

    ExpandedWrap disabled
      template <typename> struct CBinder;
      template <size_t... Indexes>
      struct CBinder<std::index_sequence<Indexes...>>
      {
          template <typename TFunc, typename... TT>
          static auto Bind(TFunc &&func, TT&&... args)
          {
              typedef std::tuple
              <
                  decltype(std::placeholders::_1),
                  decltype(std::placeholders::_2),
                  decltype(std::placeholders::_3),
                  decltype(std::placeholders::_4),
                  decltype(std::placeholders::_5),
                  decltype(std::placeholders::_6),
                  decltype(std::placeholders::_7),
                  decltype(std::placeholders::_8),
                  decltype(std::placeholders::_9),
                  decltype(std::placeholders::_10)
              > TPlaceholders;
       
              return std::bind(std::forward<TFunc>(func), std::forward<TT>(args)...,
                  typename std::tuple_element<Indexes, TPlaceholders>::type{}...);
          }
      };
       
      template <typename TRes, typename... TArgs>
      class CFunction
      {
      public:
          typedef std::unique_ptr<CFunction> TPtr;
       
          template <typename TFunc, typename... TT>
          static TPtr Make(TFunc &&func, TT&&... args)
          {
              typedef decltype(std::make_index_sequence<sizeof...(TArgs)>()) TArgsIndexes;
       
              auto fn = CBinder<TArgsIndexes>::Bind(std::forward<TFunc>(func), std::forward<TT>(args)...);
              return std::make_unique<CFunctionImpl<decltype(fn), CFunction>>(std::move(fn));
          }
       
          virtual ~CFunction() {;}
       
          template <typename... TT>
          TRes Call(TT&&... args)
          {
              return _Call(std::forward<TT>(args)...);
          }
       
          template <typename... TT>
          TRes operator()(TT&&... args)
          {
              return Call(std::forward<TT>(args)...);
          }
       
      protected:
          virtual TRes _Call(TArgs... args) = 0;
       
          template <typename TFunc, typename T>
          class CFunctionImpl
          : public T
          {
          public:
              CFunctionImpl(TFunc &&fn)
              : m_fn(std::forward<TFunc>(fn))
              {
              }
       
              virtual TRes _Call(TArgs... args) override
              {
                  return m_fn(args...);
              }
          protected:
              TFunc m_fn;
          };
      };


    Использование:

    ExpandedWrap disabled
      static
      void TestFunc(const char *__file__, int __line__, size_t p1, const char *p2)
      {
          std::cout << p1 << std::endl;
          std::cout << p2 << std::endl;
          std::cout << __file__ << std::endl;
          std::cout << __line__ << std::endl;
      }
       
      ........................
       
      typedef CFunction<void, size_t, const char *> TFunc;
      auto spFunc = TFunc::Make(TestFunc, __FILE__, __LINE__);
      spFunc->Call(111, "22222");
       
      std::unique_ptr<std::string> sp(new std::string("!!!!!!!!!!"));
      auto spFunc2 = TFunc::Make([sp = std::move(sp)](size_t p1, const char *p2)
      {
          std::cout << p1 << std::endl;
          std::cout << p2 << std::endl;
          std::cout << *sp << std::endl;
      });
      spFunc2->Call(111, "22222");


    Но, что-то такая реализация мне не очень нравится, в основном из-за лишнего вызова виртуального метода.
    Подозреваю, что есть "более лучшее" решение, но не могу придумать ничего умного.
    Сообщение отредактировано: Олег М -
      Цитата Олег М @
      Известно, что для std::function обязательно требуется конструктор копирования.

      Можно подробнее? На сколько я знаю std::function можно присваивать даже лямбды, которые даже в теории не имеют конструкторы копирования.
      Если что - мне просто интересно. Я работал с std::function, но мало наверное. И не припомню ограничения с конструктором копирования.
      Сообщение отредактировано: KILLER -
        Цитата KILLER @
        Можно подробнее? На сколько я знаю std::function можно присваивать даже лямбды, которые даже в теории не имеют конструкторы копирования.

        Вот примеры, лямбдами и без.
        Выдают ошибку error: use of deleted function ...., на конструктор копирования.
        Если в них заменить unique_ptr на shared_ptr, то всё будет нормально
        ExpandedWrap disabled
          static
          void TestFunc(std::unique_ptr<std::string> &sp, const std::string &s)
          {
          }
           
          typedef std::function<void(const std::string &)> TFunc;
           
          std::unique_ptr<std::string> sp(new std::string()) ;
           
          TFunc fn = std::bind(TestFunc, std::move(sp), std::placeholders::_1);
          fn("1111111111111111111111");
           
          TFunc fn2 = std::bind([](std::unique_ptr<std::string> &sp, const std::string &s)
          {
          }, std::move(sp), std::placeholders::_1);
          fn2("1111111111111111111111");
           
          TFunc fn3 = std::bind([sp = std::move(sp)](const std::string &s)
          {
          }, std::placeholders::_1);
          fn3("1111111111111111111111");
          Там проблема именно при инициализации объекта std::function тем объектом, который возвращает std::bind
          ExpandedWrap disabled
            static void TestFunc(std::unique_ptr<std::string> &sp, const std::string &s)
            {
                std::cout << *sp << " | " << s << std::endl;
            }
            typedef std::function<void(const std::string &)> TFunc;
            typedef std::function<void(std::unique_ptr<std::string>&, const std::string &)> TFunc2;
             
            static void Test1()
            {
                std::unique_ptr<std::string> sp(new std::string("0"));
                auto lambda = [&sp] (const std::string &s) { TestFunc(sp, s); };
                // std::function инициализируется лямбда-функцией
                // откомпилируется нормально
                TFunc func_obj1 = lambda;
                func_obj1("1");
                // std::function инициализируется указателем на функцию
                // откомпилируется нормально
                TFunc2 func_obj2 = TestFunc;
                func_obj2(sp, "2");
                // объект, возвращаемый std::bind
                // откомпилируется нормально
                auto bind_obj = std::bind(TestFunc, std::move(sp), std::placeholders::_1);
                bind_obj("3");
                // а вот это, действительно, не компилируется
                TFunc fn = bind_obj;
            }

          Почему так происходит, надо разбираться. Только там в исходном коде std черт ногу сломит :-)

          Цитата
          лямбды, которые даже в теории не имеют конструкторы копирования

          В смысле? Лямбду же можно скопировать:
          ExpandedWrap disabled
            auto lambda = [&sp] (const std::string &s) { TestFunc(sp, s); };
            auto lambda2 = lambda;
            Цитата Serpentus @
            Почему так происходит, надо разбираться. Только там в исходном коде std черт ногу сломит :-)

            Не обязательно копаться в исходном коде. Там вроде заявлено, что std::function должна быть CopyConstructable. Хотелось бы знать - для чего?
              Цитата Олег М @
              Хотелось бы знать - для чего?

              Так она реализована :-) Вот конструктор, который используется в данном случае
              ExpandedWrap disabled
                    template<class _Fx,
                        class _Inv_res = typename _Mybase::template _Result_of_invoking_t<_Fx&>,
                        class = typename _Mybase::template _Enable_if_returnable_t<_Inv_res> >
                        function(_Fx _Func)
                        {   // construct wrapper holding copy of _Func
                        this->_Reset(_STD move(_Func));
                        }

              Комментарий четко дает понять, что с параметра, переданного при инициализации, чем бы он ни был, снимается и сохраняется копия. Тот объект, который возвращает std::bind, копирование не позволяет, отсюда результат. Речь идет про тот частный случай std::bind, в другом случае эта функция может возвращать копируемый объект.
              ExpandedWrap disabled
                auto bind_obj = std::bind(TestFunc, std::move(sp), std::placeholders::_1);
                auto bind_obj2 = bind_obj; // Не скомпилируется
                 
                static void foo(int i) {}
                static void Test()
                {
                    auto bind_obj = std::bind(foo, 1);
                    auto bind_obj2 = bind_obj; // OK
                }
                Цитата Serpentus @
                Комментарий четко дает понять, что с параметра, переданного при инициализации, чем бы он ни был, снимается и сохраняется копия

                Что–то не вижу я там копирования, вижу move.
                Ну и по–любому не понимаю – для чего там может понадобится обязательное копирование?
                  Цитата Олег М @
                  Там вроде заявлено, что std::function должна быть CopyConstructable. Хотелось бы знать - для чего?
                  Очевидно, что функциональные объекты интуитивно должны отвечать привычной программисту семантике значений. Так же как std::shared_ptr<> реализует это для указателей. Биндить в них значения суть нормальное явление, иначе зачем вообще использовать объекты вместо просто функций, однако это и подразумевает CopyConstructable для биндов.
                  Пытаться сделать из не CopyConstructable объекта, того же std::uniqur_ptr<>, например, CopyConstructable объект не есть хорошая идея. Я бы не хотел какого-нибудь damned hell в своём проекте. Это когда в ряде случаев объекты вдруг оказываются пусты, потому что перемещены вовнутрь неведомых дебрей, и теперь живут там... или уже нет, хрен разберёшь. Иметь две разных семантики для одной парадигмы функционального объекта будет провоцировать на мат в дальнейшем. Лучше просто отказаться от биндов не CopyConstructable значений.
                    Цитата Qraizer @
                    Лучше просто отказаться от биндов не CopyConstructable значений.

                    В смысле, отказаться от передачи unique_ptr в поток или в callback? Или рассчитывать, что будут делаться копии твоих списков, строк и т.д.?
                      В случае с потоками, вроде разобрался. Там прикол в том, что в отличие от std::bind, функция вызывается только один раз. И желательно, чтоб сразу после вызова все параметры удалялись, да и сама функция тоже. Чего, кстати, std::thread вроде не делает.
                      Написал, специально для потоков, вот такую штуку, не уверен, правда, что правильно

                      ExpandedWrap disabled
                        template <typename> struct CThreadProcInvoker;
                         
                        template <size_t... Indexes>
                        struct CThreadProcInvoker<std::index_sequence<Indexes...>>
                        {
                            template <typename TFunc, typename TArgs, typename... TT>
                            static void Invoke(TFunc func, TArgs args, TT&&... args2)
                            {
                                std::invoke(func, std::move(std::get<Indexes>(args))..., std::forward<TT>(args2)...);
                            }
                        };
                         
                        template <typename TFunc, typename... TArgs>
                        class CThreadProc
                        {
                        public:
                            explicit CThreadProc(TFunc &&fn, TArgs&&... args)
                            : m_fn(std::forward<TFunc>(fn))
                            , m_args(std::forward<TArgs>(args)...)
                            {
                            }
                         
                            CThreadProc(CThreadProc &&) = default;
                            CThreadProc& operator =(CThreadProc &&) = default;
                         
                            CThreadProc(const CThreadProc &) = delete;
                            CThreadProc& operator =(const CThreadProc &) = delete;
                         
                            template <typename... TT> requires std::is_move_constructible<TFunc>::value
                            void operator()(TT&&... args)
                            {
                                CThreadProcInvoker<std::index_sequence_for<TArgs...>>::Invoke(std::move(m_fn), std::move(m_args), std::forward<TT>(args)...);
                            }
                         
                        protected:
                            TFunc m_fn;
                            std::tuple<std::decay_t<TArgs>...> m_args;
                        };
                         
                        template <typename TFunc, typename... TArgs>
                        auto MakeThreadProc(TFunc &&fn, TArgs&&... args)
                        {
                            return CThreadProc<TFunc, TArgs...>(std::forward<TFunc>(fn), std::forward<TArgs>(args)...);
                        }
                        Цитата Олег М @
                        Что–то не вижу я там копирования, вижу move.

                        move - это для передачи параметра в _Reset, копирование, очевидно, происходит в ней.
                          Цитата Serpentus @
                          move - это для передачи параметра в _Reset, копирование, очевидно, происходит в ней.

                          Всё равно непонятно, для чего там нужно копирование.
                            Олег М
                            Копирование нужно, чтобы сохранить нечто, которым мы инициализируем std::function, где-то там у себя в члене класса. Надо запомнить то, что мы потом вызывать будем. Не ссылку же на него сохранять.
                            Я говорю, по исходникам лазить - умрешь, но можно предположить, что там что-то вроде такого (очень упрощенно):
                            ExpandedWrap disabled
                              struct _CallableBase {
                                virtual ~_CallableBase() = default;
                              };
                               
                              template<typename T>
                              struct _Callable : public _CallableBase, public T {
                              };
                               
                              template<...>
                              class function {
                                ...
                                template<typename TFunc>
                                function(TFunc func)
                                {
                                  // сохраняем что-то, что потом можно вызывать через operator ()
                                  call_obj = new _Callable<TFunc>(func); // тут с func надо снять копию
                                }
                                ...
                              private:
                                _CallableBase* call_obj;
                              };
                              Цитата Serpentus @
                              но можно предположить, что там что-то вроде такого (очень упрощенно):

                              У меня разве не то же самое, только без копии?
                                Олег М
                                Так вопрос же вроде был, зачем в std::funtion требование CopyConstructable, не? Или ты имеешь в виду, почему они не сделали что-то типа того, как у тебя? Это можно только гадать. Может просто не додумались. :) Или не посчитали нужным (или даже вредным, Qraizer объяснил почему). А еще у вызова через std::function нулевой оверхед, а у вызова через std::unique_ptr<CFunction> - нет (пусть и не большой).
                                  Цитата Serpentus @
                                  Это можно только гадать. Может просто не додумались. Или не посчитали нужным (или даже вредным, Qraizer объяснил почему).

                                  Всегда должны быть причины. Очень сильно сомневаюсь, что они там до чего-то не додумались.

                                  Цитата Serpentus @
                                  . А еще у вызова через std::function нулевой оверхед, а у вызова через std::unique_ptr<CFunction> - нет (пусть и не большой).

                                  Подозреваю, что std::function реализована точно также - виртуальный метод, динамическое создание класса и т.д. Т.е. оверхед абсолютно такой же.
                                    В общем, остановился вот на таком варианте
                                    ExpandedWrap disabled
                                      template <typename> struct CFunctionInvoker;
                                      template <size_t... Indexes>
                                      struct CFunctionInvoker<std::index_sequence<Indexes...>>
                                      {
                                          template <typename TFunc, typename TArgs, typename... TT>
                                          static auto Invoke(TFunc &&func, TArgs &&args, TT&&... args2)
                                          {
                                              return std::invoke(func, std::get<Indexes>(args)..., std::forward<TT>(args2)...);
                                          }
                                       
                                          template <typename TFunc, typename TArgs, typename... TT>
                                          static auto InvokeOnce(TFunc func, TArgs args, TT&&... args2)
                                          {
                                              return std::invoke(func, std::move(std::get<Indexes>(args))..., std::forward<TT>(args2)...);
                                          }
                                       
                                          template <typename TFunc, typename... TT>
                                          static auto Invoke(TFunc &&func, std::tuple<>, TT&&... args2)
                                          {
                                              return std::invoke(func, std::forward<TT>(args2)...);
                                          }
                                       
                                          template <typename TFunc, typename TArgs, typename... TT>
                                          static auto InvokeOnce(TFunc func, std::tuple<>, TT&&... args2)
                                          {
                                              return std::invoke(func, std::forward<TT>(args2)...);
                                          }
                                       
                                      };
                                       
                                      template <typename TRes, typename... TArgs>
                                      class CFunction
                                      {
                                      public:
                                          typedef std::unique_ptr<CFunction> TPtr;
                                       
                                          template <typename TFunc, typename... TT>
                                          static TPtr Make(TFunc &&func, TT&&... args)
                                          {
                                              return std::make_unique<CFunctionImpl<CFunction, TFunc, TT...>>(std::forward<TFunc>(func), std::forward<TT>(args)...);
                                          }
                                       
                                          virtual ~CFunction() {;}
                                       
                                          template <typename... TT>
                                          TRes Call(TT&&... args)
                                          {
                                              return _Call(std::forward<TT>(args)...);
                                          }
                                       
                                          template <typename... TT>
                                          TRes operator()(TT&&... args)
                                          {
                                              return Call(std::forward<TT>(args)...);
                                          }
                                       
                                      protected:
                                          virtual TRes _Call(TArgs... args) = 0;
                                       
                                          template <typename TFunction, typename TFunc, typename... TT>
                                          class CFunctionImpl
                                          : public TFunction
                                          {
                                          public:
                                              CFunctionImpl(TFunc &&fn, TT&&... args)
                                              : m_fn(std::forward<TFunc>(fn))
                                              , m_args(std::forward<TT>(args)...)
                                              {
                                              }
                                       
                                              virtual TRes _Call(TArgs... args) override
                                              {
                                                  return CFunctionInvoker<std::index_sequence_for<TT...>>::Invoke(m_fn, m_args, args...);
                                              }
                                          protected:
                                              TFunc m_fn;
                                              std::tuple<std::decay_t<TT>...> m_args;
                                          };
                                       
                                      };


                                    Избавился от std::placeholders. На большее меня не хватает.
                                      Цитата Serpentus @
                                      ...Это можно только гадать. Может просто не додумались. :) Или не посчитали нужным (или даже вредным, Qraizer объяснил почему).
                                      Я уверен, что правильно указал причину. Во всех официальных вариантах реализаций, что в крайне скудном функционале C++98, что в более мощном варианте, появившемся в бусте, функциональные объекты полностью отвечали семантике значений. Их можно было свободно и копировать, и присваивать, в общем делать всё то, что обычный программист привык делать с обычными переменными. Если б в C++11 решили, что это не нужно, программисты б сильно удивились.
                                        Цитата Qraizer @
                                        Их можно было свободно и копировать, и присваивать, в общем делать всё то, что обычный программист привык делать с обычными переменными.

                                        Там не в копировании проблема, а отсутствии move
                                          Так я как раз об этом. Копирование и перемещение – это разные семантики. Я бы не хотел вдруг узнать что после a=b источник разрушен. Конечно, по дефолту так и не будет, ибо нужно хотя бы a=std::move(b) написать. Но дело не в этом. Из-за документированной семантики копирования куча библиотек уже использовали функциональные объекты в части своей реализации. И как именно, одним только их разработчикам ведомо, и то не факт. Если теперь весь использующий их софт вдруг начнёт использовать std::function с поддержкой объектов, использующих только семантику перемещения, один чёрт знает, как это может аукнуться даже на самих таких библиотеках, не говоря уже об этом софте.

                                          Добавлено
                                          В общем, если это свой собственный велосипед, это ещё куда ни шло, хотя бы видно, что не std. Но в std такую фичу вводить вряд ли будут, опасно это.
                                            Цитата Qraizer @
                                            Так я как раз об этом. Копирование и перемещение – это разные семантики.

                                            Никто с этим не спорит. Проблема в том, что там ты ничего не копируешь.
                                            Где ты видишь копирование в std::function<....> fn = std::bind(.....)?
                                            Т.е. std::function обязательно требует наличие конструктора копирования для rvalue. Зачем?

                                            Цитата Qraizer @
                                            Если теперь весь использующий их софт вдруг начнёт использовать std::function с поддержкой объектов, использующих только семантику перемещения, один чёрт знает, как это может аукнуться даже на самих таких библиотеках, не говоря уже об этом софте.

                                            Что значит "только семантику перемещения"? Контейнеры только её используют? Чем std::function отличается от них?
                                              Цитата Олег М @
                                              Подозреваю, что std::function реализована точно также - виртуальный метод, динамическое создание класса и т.д. Т.е. оверхед абсолютно такой же.

                                              Зачем подозревать? Просто откомпилируй и в отладчике на месте вызовов функций посмотри в Disassembly: в вызове std::function поверх обычной функции ровно те же инструкции, что и при вызове самой функции, в твоем коде инструкций больше. Оверхед небольшой и в 99+% случаев на него можно забить, но в STL стараются все оптимизировать по максимуму.
                                                Цитата Serpentus @
                                                Зачем подозревать? Просто откомпилируй и в отладчике на месте вызовов функций посмотри в Disassembly: в вызове std::function поверх обычной функции ровно те же инструкции, что и при вызове самой функции, в твоем коде инструкций больше. Оверхед небольшой и в 99+% случаев на него можно забить, но в STL стараются все оптимизировать по максимуму.

                                                А ты std::function инициализировал через std::bind с параметрами?
                                                  Цитата Олег М @
                                                  Т.е. std::function обязательно требует наличие конструктора копирования для rvalue. Зачем?
                                                  :facepalm: Проехали.
                                                  Цитата Олег М @
                                                  Что значит "только семантику перемещения"? Контейнеры только её используют? Чем std::function отличается от них?
                                                  Контейнеры крайне редко использовались программистами в роли обычных переменных обычным для программиста способом, т.к. все понимали, что их копирование суть тяжёлая операция и всячески старались это оптимизировать, а уж для библиотечного кода и подавно.
                                                    Цитата Qraizer @
                                                    Проехали.

                                                    И что же мы проехали?

                                                    Насколько мне известно, std::function появилась в 11-м стандарте, где move-семантика уже вовсю использовалась. О каком старом коде и о каких привычках программистов может идти речь?
                                                    Объясни мне, почему вот этот код компилируется
                                                    ExpandedWrap disabled
                                                      std::unique_ptr<std::string> sp(new std::string("!!!!!!!!!!!!!!!!!"));
                                                      auto fn = [sp = std::move(sp)]()
                                                      {
                                                          std::cout << *sp << std::endl;
                                                      };
                                                       
                                                      auto fn2 = std::move(fn);
                                                      fn2();


                                                    А вот этот - нет
                                                    ExpandedWrap disabled
                                                      std::unique_ptr<std::string> sp(new std::string("!!!!!!!!!!!!!!!!!"));
                                                      std::function<void()> fn = [sp = std::move(sp)]()
                                                      {
                                                          std::cout << *sp << std::endl;
                                                      };


                                                    Хотя принципиального отличия лично я не вижу.


                                                    Цитата Qraizer @
                                                    Контейнеры крайне редко использовались программистами в роли обычных переменных обычным для программиста способом, т.к. все понимали, что их копирование суть тяжёлая операция и всячески старались это оптимизировать, а уж для библиотечного кода и подавно.

                                                    Т.е. копирование обычной переменной это считается нормальным? Или я как-то не понимаю, что значит "обычная переменная"?
                                                      Цитата Олег М @
                                                      Объясни мне, почему вот этот код компилируется
                                                      А вот этот - нет
                                                      Хотя принципиального отличия лично я не вижу.

                                                      Потому что в первом случае - некий сгенерированный компилятором класс, который (в данном случае) будет Moveable, но не CopyConstructable (согласно требованиям стандарта), а во втором - std::function, со строго специфицированным внешним поведением. Ну не может у тебя std::function быть в одном случае копируемым, а в другом - перемещаемым (в зависимости от того, чем его инициализировали). А вот лямбда-класс - может, потому что безымянный с минимально специфицированным публичным интерфейсом.
                                                        Да уж. Дошло наконец-то.
                                                        Т.е. действительно, здесь без виртуального метода не обойтись.
                                                          :D Фуф. :D
                                                            Цитата Олег М @
                                                            А ты std::function инициализировал через std::bind с параметрами?

                                                            И так, и сяк. Двоичный код при вызове std::function идентичен коду при вызове того, чем он проинициализирован. Это ожидаемо, потому что std::function копирует внутрь себя объект, которым инициализируется, после что вызывает эту копию. (Под вызовом я имею в виду вызов "operator ()").
                                                            Сообщение отредактировано: Serpentus -
                                                              Цитата Serpentus @
                                                              И так, и сяк. Двоичный код при вызове std::function идентичен коду при вызове того, чем он проинициализирован. Это ожидаемо, потому что std::function копирует внутрь себя объект, которым инициализируется, после что вызывает эту копию. (Под вызовом я имею в виду вызов "operator ()").

                                                              Я думаю, если инициализация std::function и использование будут в разных единицах трансляции - то код будет немного другим, с косвенным вызовом. Но я могу ошибаться.
                                                                Цитата Serpentus @
                                                                И так, и сяк. Двоичный код при вызове std::function идентичен коду при вызове того, чем он проинициализирован. Это ожидаемо, потому что std::function копирует внутрь себя объект, которым инициализируется, после что вызывает эту копию. (Под вызовом я имею в виду вызов "operator ()").

                                                                Т.е. это надо выделить память, вызвать для неё конструктор и сохранить где-то ссылки на деструктор и оператор(), так наверное?
                                                                  Flex Ferrum
                                                                  ИМХО в случае простого указателя на функцию код все равно будет одним и тем же: "положить" параметры, загрузить в регистр адрес функции (не важно, переменная-указатель это или числовой литерал) и совершить переход. Для функциональных объектов - не знаю, возможно. Но я не проверял, так что утверждать не берусь.

                                                                  Олег М
                                                                  Так, но это на создание/удаление. А на вызов оверхед нулевой.
                                                                    Цитата Serpentus @
                                                                    Так, но это на создание/удаление. А на вызов оверхед нулевой.

                                                                    В смысле, нулевой? Я так понимаю - нулевой, это когда inline. А здесь будет вызов обычного метода.
                                                                      Олег М
                                                                      В смысле по сравнению с вызовом того, чем проинициализировали std::function.
                                                                        Цитата Serpentus @
                                                                        В смысле по сравнению с вызовом того, чем проинициализировали std::function.

                                                                        Ну да, так и должно быть.
                                                                        Тут вот тоже интересно - как в bind эти placeholders разруливаются, ещё одно tuple строится, со ссылками? Или как-то по-другому?
                                                                        0 пользователей читают эту тему (0 гостей и 0 скрытых пользователей)
                                                                        0 пользователей:


                                                                        Рейтинг@Mail.ru
                                                                        [ Script execution time: 0,0944 ]   [ 17 queries used ]   [ Generated: 29.03.24, 10:05 GMT ]