亚洲在线久爱草,狠狠天天香蕉网,天天搞日日干久草,伊人亚洲日本欧美

為了賬號安全,請及時綁定郵箱和手機立即綁定
已解決430363個問題,去搜搜看,總會有你想問的

如何從捕獲移動的lambda表達式中創建std :: function?

如何從捕獲移動的lambda表達式中創建std :: function?

C++
慕田峪9158850 2019-10-11 11:11:13
我正在嘗試std::function從捕獲移動的lambda表達式創建一個。請注意,我可以毫無問題地創建一個捕獲移動的lambda表達式;只有當我嘗試將其包裝為時std::function,我才會收到錯誤消息。例如:auto pi = std::make_unique<int>(0);// no problems here!auto foo = [q = std::move(pi)] {    *q = 5;    std::cout << *q << std::endl;};// All of the attempts below yield:// "Call to implicitly-deleted copy constructor of '<lambda...."std::function<void()> bar = foo;std::function<void()> bar{foo};std::function<void()> bar{std::move(foo)};std::function<void()> bar = std::move(foo);std::function<void()> bar{std::forward<std::function<void()>>(foo)};std::function<void()> bar = std::forward<std::function<void()>>(foo);我將解釋為什么我要寫這樣的東西。我寫了一個UI庫,類似于jQuery的或JavaFX的,允許用戶通過傳遞給處理鼠標/鍵盤事件std::functions到方法有相似的名字on_mouse_down(),on_mouse_drag(),push_undo_action(),等。顯然,std::function我想傳入的參數在理想情況下應使用捕獲移動的lambda表達式,否則我需要訴諸在C ++ 11為標準時使用的難看的“ release / acquire-in-lambda”習慣用法:std::function<void()> baz = [q = pi.release()] {    std::unique_ptr<int> p{q};    *p = 5;    std::cout << *q << std::endl;};請注意,baz兩次調用將是上述代碼中的錯誤。但是,在我的代碼中,此閉包被保證只被調用一次。順便說一句,在我的真實代碼中,我沒有傳遞std::unique_ptr<int>,而是更有趣的東西。最后,我正在使用Xcode6-Beta4,它使用以下版本的clang:Apple LLVM version 5.1 (clang-503.0.40) (based on LLVM 3.4svn)Target: x86_64-apple-darwin13.3.0Thread model: posix
查看完整描述

3 回答

?
拉丁的傳說

TA貢獻1789條經驗 獲得超8個贊

template<class F> function(F f);


template <class F, class A> function(allocator_arg_t, const A& a, F f);


要求: F應為CopyConstructible。f應該Callable用于參數類型ArgTypes和返回類型R。A的復制構造函數和析構函數不得拋出異常。


§20.9.11.2.1[func.wrap.func.con]


請注意,operator =是根據此構造函數和定義的swap,因此存在相同的限制:


template<class F> function& operator=(F&& f);


效果: function(std::forward<F>(f)).swap(*this);


§20.9.11.2.1[func.wrap.func.con]


因此,要回答你的問題:是的,這是可以構建一個std::function從一招捕獲拉姆達(因為這僅指定拉姆達如何捕捉),但它是不是可以構建一個std::function從唯才是舉型(如MOVE-捕獲lambda,該lambda移動捕獲無法復制構造的內容)。


查看完整回答
反對 回復 2019-10-11
?
慕妹3242003

TA貢獻1824條經驗 獲得超6個贊

由于std::function<?>必須對存儲的可調用對象的副本構造函數進行類型擦除,因此不能從僅移動類型構造它。您的lambda因為它按值捕獲僅移動類型,所以它是僅移動類型。所以...您無法解決您的問題。  std::function無法存儲您的lambda。


至少不是直接。


這是C ++,我們只是解決問題。


template<class F>

struct shared_function {

  std::shared_ptr<F> f;

  shared_function() = delete; // = default works, but I don't use it

  shared_function(F&& f_):f(std::make_shared<F>(std::move(f_))){}

  shared_function(shared_function const&)=default;

  shared_function(shared_function&&)=default;

  shared_function& operator=(shared_function const&)=default;

  shared_function& operator=(shared_function&&)=default;

  template<class...As>

  auto operator()(As&&...as) const {

    return (*f)(std::forward<As>(as)...);

  }

};

template<class F>

shared_function< std::decay_t<F> > make_shared_function( F&& f ) {

  return { std::forward<F>(f) };

}

至此,我們可以解決您的問題。


auto pi = std::make_unique<int>(0);


auto foo = [q = std::move(pi)] {

  *q = 5;

  std::cout << *q << std::endl;

};


std::function< void() > test = make_shared_function( std::move(foo) );

test(); // prints 5

a的語義與shared_function其他函數略有不同,因為它的副本std::function與原始函數具有相同的狀態(包括變成a時)。


我們還可以編寫僅移動一次觸發函數:


template<class Sig>

struct fire_once;


template<class T>

struct emplace_as {};


template<class R, class...Args>

struct fire_once<R(Args...)> {

  // can be default ctored and moved:

  fire_once() = default;

  fire_once(fire_once&&)=default;

  fire_once& operator=(fire_once&&)=default;


  // implicitly create from a type that can be compatibly invoked

  // and isn't a fire_once itself

  template<class F,

    std::enable_if_t<!std::is_same<std::decay_t<F>, fire_once>{}, int> =0,

    std::enable_if_t<

      std::is_convertible<std::result_of_t<std::decay_t<F>&(Args...)>, R>{}

      || std::is_same<R, void>{},

      int

    > =0

  >

  fire_once( F&& f ):

    fire_once( emplace_as<std::decay_t<F>>{}, std::forward<F>(f) )

  {}

  // emplacement construct using the emplace_as tag type:

  template<class F, class...FArgs>

  fire_once( emplace_as<F>, FArgs&&...fargs ) {

    rebind<F>(std::forward<FArgs>(fargs)...);

  }

  // invoke in the case where R is not void:

  template<class R2=R,

    std::enable_if_t<!std::is_same<R2, void>{}, int> = 0

  >

  R2 operator()(Args...args)&&{

    try {

      R2 ret = invoke( ptr.get(), std::forward<Args>(args)... );

      clear();

      return ret;

    } catch(...) {

      clear();

      throw;

    }

  }

  // invoke in the case where R is void:

  template<class R2=R,

    std::enable_if_t<std::is_same<R2, void>{}, int> = 0

  >

  R2 operator()(Args...args)&&{

    try {

      invoke( ptr.get(), std::forward<Args>(args)... );

      clear();

    } catch(...) {

      clear();

      throw;

    }

  }


  // empty the fire_once:

  void clear() {

    invoke = nullptr;

    ptr.reset();

  }


  // test if it is non-empty:

  explicit operator bool()const{return (bool)ptr;}


  // change what the fire_once contains:

  template<class F, class...FArgs>

  void rebind( FArgs&&... fargs ) {

    clear();

    auto pf = std::make_unique<F>(std::forward<FArgs>(fargs)...);

    invoke = +[](void* pf, Args...args)->R {

      return (*(F*)pf)(std::forward<Args>(args)...);

    };

    ptr = {

      pf.release(),

      [](void* pf){

        delete (F*)(pf);

      }

    };

  }

private:

  // storage.  A unique pointer with deleter

  // and an invoker function pointer:

  std::unique_ptr<void, void(*)(void*)> ptr{nullptr, +[](void*){}};

  void(*invoke)(void*, Args...) = nullptr;

};

通過emplace_as<T>標簽甚至支持不可移動的類型。


請注意,您必須()在右值上下文中進行評估(即,在后面std::move),因為沉默的破壞性行為()似乎是不禮貌的。


此實現不使用SBO,因為如果這樣做,它將要求存儲的類型是可移動的,并且(對我來說)引導將需要更多工作。


查看完整回答
反對 回復 2019-10-11
?
心有法竹

TA貢獻1866條經驗 獲得超5個贊

這是一個更簡單的解決方案:


   auto pi = std::make_unique<int>(0);


   auto ppi = std::make_shared<std::unique_ptr<int>>(std::move(pi));


   std::function<void()> bar = [ppi] {

        **ppi = 5;

        std::cout << **ppi << std::endl;

   };


查看完整回答
反對 回復 2019-10-11
  • 3 回答
  • 0 關注
  • 860 瀏覽

添加回答

舉報

0/150
提交
取消
微信客服

購課補貼
聯系客服咨詢優惠詳情

幫助反饋 APP下載

慕課網APP
您的移動學習伙伴

公眾號

掃描二維碼
關注慕課網微信公眾號