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移動捕獲無法復制構造的內容)。

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,因為如果這樣做,它將要求存儲的類型是可移動的,并且(對我來說)引導將需要更多工作。

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;
};
- 3 回答
- 0 關注
- 860 瀏覽
添加回答
舉報